././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1755966309.4669907
pyrfc3339-2.1.0/ 0000755 0001751 0000166 00000000000 15052365545 012574 5 ustar 00runner docker ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1755966304.0
pyrfc3339-2.1.0/.gitignore 0000644 0001751 0000166 00000005124 15052365540 014561 0 ustar 00runner docker # Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# pyenv
.python-version
# celery beat schedule file
celerybeat-schedule
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/dictionaries
.idea/**/shelf
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# CMake
cmake-build-debug/
cmake-build-release/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1755966304.0
pyrfc3339-2.1.0/CHANGES.rst 0000644 0001751 0000166 00000002777 15052365540 014406 0 ustar 00runner docker Changelog
=========
2.1.0 (2025-08-23)
------------------
(The Git branch used for development was renamed from ``master`` to ``main``)
- Greatly simplify timestamp parsing and generation code, leveraging improvements in native Python
capabilities in the past decade-and-a-half. See :commit:`53c2d15` for further details.
- Simplify GitHub Actions workflow. Add `zizmor `_ to audit workflows.
- Modernize packaging and documentation configuration.
2.0.1 (2024-11-03)
------------------
- Fix GitHub Actions badge in README.rst.
2.0 (2024-11-03)
----------------
(not released to PyPI)
- Migrate tests from ``nose`` to ``unittest`` and ``pytest`` (:issue:`16`)
- Replace :mod:`pytz` dependency with :attr:`datetime.timezone.utc` and :mod:`zoneinfo` (:issue:`15`)
- Reformat codebase with ``black`` and ``isort``
- Configure GitHub Actions; remove Travis CI configuration file
1.1 (2018-06-10)
----------------
- Drop support for EOL Python releases, add (explicit) support for Python 3.5 and 3.6 (:issue:`7,10,11,12`)
- Add :meth:`.utils.FixedOffset.__deepcopy__()` method, to prevent crash on deepcopy (:issue:`8`)
1.0 (2015-11-09)
----------------
- First formally-tagged release
- Fix :func:`.utils.timedelta_seconds()` to use :meth:`datetime.timedelta.total_seconds()` when the native method is available (:issue:`6`)
- Documentation and packaging cleanup (:issue:`4,5`)
0.2 (2014-02-09)
----------------
- Python 3 compatibility (:issue:`2`)
0.1 (2011-01-26)
----------------
- Initial release
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1755966304.0
pyrfc3339-2.1.0/LICENSE.txt 0000644 0001751 0000166 00000002071 15052365540 014412 0 ustar 00runner docker Copyright (c) 2025 Contributors to the pyRFC3339 project
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.
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1755966304.0
pyrfc3339-2.1.0/MANIFEST.in 0000644 0001751 0000166 00000000111 15052365540 014316 0 ustar 00runner docker prune pyrfc3339/tests
prune .idea
prune .github
exclude .readthedocs.yml
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1755966309.4659908
pyrfc3339-2.1.0/PKG-INFO 0000644 0001751 0000166 00000005306 15052365545 013675 0 ustar 00runner docker Metadata-Version: 2.4
Name: pyRFC3339
Version: 2.1.0
Summary: Generate and parse RFC 3339 timestamps
Author-email: Kurt Raschke
License-Expression: MIT
Project-URL: Homepage, https://github.com/kurtraschke/pyrfc3339
Project-URL: Documentation, https://pyrfc3339.readthedocs.io/
Project-URL: Repository, https://github.com/kurtraschke/pyRFC3339.git
Project-URL: Bug Tracker, https://github.com/kurtraschke/pyRFC3339/issues
Project-URL: Changelog, https://github.com/kurtraschke/pyRFC3339/blob/main/CHANGES.rst
Keywords: rfc-3339,timestamp,iso-8601,datetime
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Internet
Requires-Python: >=3.9
Description-Content-Type: text/x-rst
License-File: LICENSE.txt
Dynamic: license-file
Description
===========
.. image:: https://github.com/kurtraschke/pyRFC3339/actions/workflows/python.yml/badge.svg
:target: https://github.com/kurtraschke/pyRFC3339/actions/workflows/python.yml
:alt: Build Status
.. image:: https://readthedocs.org/projects/pyrfc3339/badge/?version=latest
:target: https://pyrfc3339.readthedocs.io/en/latest/?badge=latest
:alt: Documentation Status
pyRFC3339 parses and generates :RFC:`3339`-compliant timestamps using `Python `_ `datetime.datetime `_ objects.
>>> from pyrfc3339 import generate, parse
>>> from datetime import datetime, timezone
>>> generate(datetime.now(timezone.utc)) #doctest:+ELLIPSIS
'...T...Z'
>>> parse('2009-01-01T10:01:02Z')
datetime.datetime(2009, 1, 1, 10, 1, 2, tzinfo=datetime.timezone.utc)
>>> parse('2009-01-01T14:01:02-04:00')
datetime.datetime(2009, 1, 1, 14, 1, 2, tzinfo=datetime.timezone(datetime.timedelta(days=-1, seconds=72000)))
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/main#egg=pyRFC3339-dev``
Tests as well as enforcement of code style, formatting, and type safety are run with `tox `_:
``$ tox``
To build the documentation with Sphinx:
``$ tox -e docs``
The documentation is also available online at:
https://pyrfc3339.readthedocs.io/
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1755966304.0
pyrfc3339-2.1.0/README.rst 0000644 0001751 0000166 00000003003 15052365540 014252 0 ustar 00runner docker Description
===========
.. image:: https://github.com/kurtraschke/pyRFC3339/actions/workflows/python.yml/badge.svg
:target: https://github.com/kurtraschke/pyRFC3339/actions/workflows/python.yml
:alt: Build Status
.. image:: https://readthedocs.org/projects/pyrfc3339/badge/?version=latest
:target: https://pyrfc3339.readthedocs.io/en/latest/?badge=latest
:alt: Documentation Status
pyRFC3339 parses and generates :RFC:`3339`-compliant timestamps using `Python `_ `datetime.datetime `_ objects.
>>> from pyrfc3339 import generate, parse
>>> from datetime import datetime, timezone
>>> generate(datetime.now(timezone.utc)) #doctest:+ELLIPSIS
'...T...Z'
>>> parse('2009-01-01T10:01:02Z')
datetime.datetime(2009, 1, 1, 10, 1, 2, tzinfo=datetime.timezone.utc)
>>> parse('2009-01-01T14:01:02-04:00')
datetime.datetime(2009, 1, 1, 14, 1, 2, tzinfo=datetime.timezone(datetime.timedelta(days=-1, seconds=72000)))
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/main#egg=pyRFC3339-dev``
Tests as well as enforcement of code style, formatting, and type safety are run with `tox `_:
``$ tox``
To build the documentation with Sphinx:
``$ tox -e docs``
The documentation is also available online at:
https://pyrfc3339.readthedocs.io/
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1755966309.4619906
pyrfc3339-2.1.0/docs/ 0000755 0001751 0000166 00000000000 15052365545 013524 5 ustar 00runner docker ././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1755966309.4619906
pyrfc3339-2.1.0/docs/build/ 0000755 0001751 0000166 00000000000 15052365545 014623 5 ustar 00runner docker ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1755966304.0
pyrfc3339-2.1.0/docs/build/.gitignore 0000644 0001751 0000166 00000000106 15052365540 016603 0 ustar 00runner docker # Ignore everything in this directory
*
# Except this file
!.gitignore ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1755966304.0
pyrfc3339-2.1.0/docs/requirements.txt 0000644 0001751 0000166 00000000072 15052365540 017002 0 ustar 00runner docker Sphinx==8.2.3
sphinx-issues==5.0.1
sphinx-rtd-theme==3.0.2 ././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1755966309.4639907
pyrfc3339-2.1.0/docs/source/ 0000755 0001751 0000166 00000000000 15052365545 015024 5 ustar 00runner docker ././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1755966309.4639907
pyrfc3339-2.1.0/docs/source/_static/ 0000755 0001751 0000166 00000000000 15052365545 016452 5 ustar 00runner docker ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1755966304.0
pyrfc3339-2.1.0/docs/source/_static/.gitignore 0000644 0001751 0000166 00000000000 15052365540 020423 0 ustar 00runner docker ././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1755966309.4639907
pyrfc3339-2.1.0/docs/source/_templates/ 0000755 0001751 0000166 00000000000 15052365545 017161 5 ustar 00runner docker ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1755966304.0
pyrfc3339-2.1.0/docs/source/_templates/.gitignore 0000644 0001751 0000166 00000000000 15052365540 021132 0 ustar 00runner docker ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1755966304.0
pyrfc3339-2.1.0/docs/source/changes.rst 0000644 0001751 0000166 00000000036 15052365540 017160 0 ustar 00runner docker .. include:: ../../CHANGES.rst ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1755966304.0
pyrfc3339-2.1.0/docs/source/conf.py 0000644 0001751 0000166 00000015115 15052365540 016321 0 ustar 00runner docker # -*- 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
from importlib.metadata import version as get_version
# 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', 'sphinx_issues']
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix of source filenames.
source_suffix = {'.rst': 'restructuredtext'}
# 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'2025 Contributors to the pyRFC3339 project'
# 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.
#
release: str = get_version("pyrfc3339")
version: str = ".".join(release.split('.')[:2])
# 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 = 'sphinx_rtd_theme'
# 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 = {'python': ('https://docs.python.org/3', None)}
issues_github_path = 'kurtraschke/pyrfc3339'
rst_prolog = """
.. role:: python(code)
:language: python
:class: highlight
""" ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1755966304.0
pyrfc3339-2.1.0/docs/source/doc.rst 0000644 0001751 0000166 00000000146 15052365540 016317 0 ustar 00runner docker Modules
=======
.. toctree::
:maxdepth: 2
:glob:
pyrfc3339
generator
parser
utils
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1755966304.0
pyrfc3339-2.1.0/docs/source/generator.rst 0000644 0001751 0000166 00000000273 15052365540 017541 0 ustar 00runner docker :mod:`pyrfc3339.generator` -- Generate :RFC:`3339` timestamps
=============================================================
.. automodule:: pyrfc3339.generator
:members:
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1755966304.0
pyrfc3339-2.1.0/docs/source/index.rst 0000644 0001751 0000166 00000000464 15052365540 016664 0 ustar 00runner docker .. 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.
pyRFC3339
=========
.. toctree::
:maxdepth: 3
intro
changes
doc
license ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1755966304.0
pyrfc3339-2.1.0/docs/source/intro.rst 0000644 0001751 0000166 00000000036 15052365540 016703 0 ustar 00runner docker .. include:: ../../README.rst
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1755966304.0
pyrfc3339-2.1.0/docs/source/license.rst 0000644 0001751 0000166 00000000057 15052365540 017175 0 ustar 00runner docker License
=======
.. include:: ../../LICENSE.txt ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1755966304.0
pyrfc3339-2.1.0/docs/source/parser.rst 0000644 0001751 0000166 00000000262 15052365540 017045 0 ustar 00runner docker :mod:`pyrfc3339.parser` -- Parse :RFC:`3339` timestamps
=============================================================
.. automodule:: pyrfc3339.parser
:members:
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1755966304.0
pyrfc3339-2.1.0/docs/source/pyrfc3339.rst 0000644 0001751 0000166 00000000126 15052365540 017215 0 ustar 00runner docker :mod:`pyrfc3339` -- Imports
=============================
.. automodule:: pyrfc3339
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1755966304.0
pyrfc3339-2.1.0/docs/source/utils.rst 0000644 0001751 0000166 00000000274 15052365540 016714 0 ustar 00runner docker :mod:`pyrfc3339.utils` -- Utilities for working with timestamps
===============================================================
.. automodule:: pyrfc3339.utils
:members:
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1755966309.4659908
pyrfc3339-2.1.0/pyRFC3339.egg-info/ 0000755 0001751 0000166 00000000000 15052365545 015573 5 ustar 00runner docker ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1755966309.0
pyrfc3339-2.1.0/pyRFC3339.egg-info/PKG-INFO 0000644 0001751 0000166 00000005306 15052365545 016674 0 ustar 00runner docker Metadata-Version: 2.4
Name: pyRFC3339
Version: 2.1.0
Summary: Generate and parse RFC 3339 timestamps
Author-email: Kurt Raschke
License-Expression: MIT
Project-URL: Homepage, https://github.com/kurtraschke/pyrfc3339
Project-URL: Documentation, https://pyrfc3339.readthedocs.io/
Project-URL: Repository, https://github.com/kurtraschke/pyRFC3339.git
Project-URL: Bug Tracker, https://github.com/kurtraschke/pyRFC3339/issues
Project-URL: Changelog, https://github.com/kurtraschke/pyRFC3339/blob/main/CHANGES.rst
Keywords: rfc-3339,timestamp,iso-8601,datetime
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Internet
Requires-Python: >=3.9
Description-Content-Type: text/x-rst
License-File: LICENSE.txt
Dynamic: license-file
Description
===========
.. image:: https://github.com/kurtraschke/pyRFC3339/actions/workflows/python.yml/badge.svg
:target: https://github.com/kurtraschke/pyRFC3339/actions/workflows/python.yml
:alt: Build Status
.. image:: https://readthedocs.org/projects/pyrfc3339/badge/?version=latest
:target: https://pyrfc3339.readthedocs.io/en/latest/?badge=latest
:alt: Documentation Status
pyRFC3339 parses and generates :RFC:`3339`-compliant timestamps using `Python `_ `datetime.datetime `_ objects.
>>> from pyrfc3339 import generate, parse
>>> from datetime import datetime, timezone
>>> generate(datetime.now(timezone.utc)) #doctest:+ELLIPSIS
'...T...Z'
>>> parse('2009-01-01T10:01:02Z')
datetime.datetime(2009, 1, 1, 10, 1, 2, tzinfo=datetime.timezone.utc)
>>> parse('2009-01-01T14:01:02-04:00')
datetime.datetime(2009, 1, 1, 14, 1, 2, tzinfo=datetime.timezone(datetime.timedelta(days=-1, seconds=72000)))
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/main#egg=pyRFC3339-dev``
Tests as well as enforcement of code style, formatting, and type safety are run with `tox `_:
``$ tox``
To build the documentation with Sphinx:
``$ tox -e docs``
The documentation is also available online at:
https://pyrfc3339.readthedocs.io/
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1755966309.0
pyrfc3339-2.1.0/pyRFC3339.egg-info/SOURCES.txt 0000644 0001751 0000166 00000001205 15052365545 017455 0 ustar 00runner docker .gitignore
CHANGES.rst
LICENSE.txt
MANIFEST.in
README.rst
pyproject.toml
docs/requirements.txt
docs/build/.gitignore
docs/source/changes.rst
docs/source/conf.py
docs/source/doc.rst
docs/source/generator.rst
docs/source/index.rst
docs/source/intro.rst
docs/source/license.rst
docs/source/parser.rst
docs/source/pyrfc3339.rst
docs/source/utils.rst
docs/source/_static/.gitignore
docs/source/_templates/.gitignore
pyRFC3339.egg-info/PKG-INFO
pyRFC3339.egg-info/SOURCES.txt
pyRFC3339.egg-info/dependency_links.txt
pyRFC3339.egg-info/top_level.txt
pyrfc3339/__init__.py
pyrfc3339/generator.py
pyrfc3339/parser.py
pyrfc3339/py.typed
pyrfc3339/utils.py ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1755966309.0
pyrfc3339-2.1.0/pyRFC3339.egg-info/dependency_links.txt 0000644 0001751 0000166 00000000001 15052365545 021641 0 ustar 00runner docker
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1755966309.0
pyrfc3339-2.1.0/pyRFC3339.egg-info/top_level.txt 0000644 0001751 0000166 00000000012 15052365545 020316 0 ustar 00runner docker pyrfc3339
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1755966304.0
pyrfc3339-2.1.0/pyproject.toml 0000644 0001751 0000166 00000005205 15052365540 015505 0 ustar 00runner docker [build-system]
requires = ["setuptools>=64", "setuptools_scm>=8"]
build-backend = "setuptools.build_meta"
[project]
name = "pyRFC3339"
dynamic = ["version"]
requires-python = ">= 3.9"
authors = [
{ name = "Kurt Raschke", email = "kurt@kurtraschke.com" }
]
description = "Generate and parse RFC 3339 timestamps"
readme = { file = "README.rst", content-type = "text/x-rst" }
keywords = ["rfc-3339", "timestamp", "iso-8601", "datetime"]
license = "MIT"
license-files = ["LICENSE.txt"]
classifiers = [
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Topic :: Internet"
]
[project.urls]
Homepage = "https://github.com/kurtraschke/pyrfc3339"
Documentation = "https://pyrfc3339.readthedocs.io/"
Repository = "https://github.com/kurtraschke/pyRFC3339.git"
"Bug Tracker" = "https://github.com/kurtraschke/pyRFC3339/issues"
Changelog = "https://github.com/kurtraschke/pyRFC3339/blob/main/CHANGES.rst"
[tool.setuptools]
packages = ["pyrfc3339"]
[tool.setuptools_scm]
[tool.mypy]
strict = true
[tool.flake8]
max-line-length = 132
[tool.isort]
profile = "black"
[tool.coverage.run]
omit = ["pyrfc3339/tests/*"]
[tool.tox]
requires = ["tox>=4"]
env_list = ["sort", "format", "style", "type", "3.9", "3.10", "3.11", "3.12", "3.13"]
skip_missing_interpreters = true
[tool.tox.gh.python]
"3.13" = ["sort", "format", "style", "type", "3.13"]
"3.12" = ["3.12"]
"3.11" = ["3.11"]
"3.10" = ["3.10"]
"3.9" = ["3.9"]
[tool.tox.env_run_base]
deps = ["pytest", "pytest-subtests", "pytest-cov", "tzdata;platform_system==\"Windows\""]
commands = [["pytest",
"--doctest-glob=docs/source/*.rst",
"--doctest-glob=README.rst",
"--doctest-modules",
"--doctest-continue-on-failure",
"--cov=pyrfc3339",
"."]]
[tool.tox.env.type]
skip_install = true
deps = ["mypy"]
commands = [["mypy", "pyrfc3339"]]
[tool.tox.env.sort]
skip_install = true
deps = ["isort"]
commands = [["isort", "--check", "--diff", "pyrfc3339"]]
[tool.tox.env.format]
skip_install = true
deps = ["black"]
commands = [["black", "--check", "--diff", "pyrfc3339"]]
[tool.tox.env.style]
skip_install = true
deps = ["flake8", "flake8-pyproject"]
commands = [["flake8", "pyrfc3339"]]
[tool.tox.env.docs]
deps = ["-r docs/requirements.txt"]
commands = [["sphinx-build", "-M", "html", "docs/source", "docs/build"]]
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1755966309.4659908
pyrfc3339-2.1.0/pyrfc3339/ 0000755 0001751 0000166 00000000000 15052365545 014241 5 ustar 00runner docker ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1755966304.0
pyrfc3339-2.1.0/pyrfc3339/__init__.py 0000644 0001751 0000166 00000001402 15052365540 016342 0 ustar 00runner docker """
pyRFC3339 parses and generates :RFC:`3339`-compliant timestamps using Python
:class:`datetime.datetime` objects.
>>> from pyrfc3339 import generate, parse
>>> from datetime import datetime, timezone
>>> generate(datetime.now(timezone.utc)) #doctest:+ELLIPSIS
'...T...Z'
>>> parse('2009-01-01T10:01:02Z')
datetime.datetime(2009, 1, 1, 10, 1, 2, tzinfo=datetime.timezone.utc)
>>> parse('2009-01-01T14:01:02-04:00')
datetime.datetime(2009, 1, 1, 14, 1, 2, tzinfo=datetime.timezone(datetime.timedelta(days=-1, seconds=72000)))
"""
from importlib.metadata import PackageNotFoundError, version
from .generator import generate
from .parser import parse
try:
__version__ = version("pyrfc3339")
except PackageNotFoundError:
pass
__all__ = ["generate", "parse"]
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1755966304.0
pyrfc3339-2.1.0/pyrfc3339/generator.py 0000644 0001751 0000166 00000006416 15052365540 016603 0 ustar 00runner docker import re
from datetime import datetime, timezone
def generate(
dt: datetime,
utc: bool = True,
accept_naive: bool = False,
microseconds: bool = False,
) -> str:
"""
Generate an :RFC:`3339`-formatted timestamp from a :class:`datetime.datetime`.
>>> from datetime import datetime, timezone
>>> from zoneinfo import ZoneInfo
>>> generate(datetime(2009, 1, 1, 12, 59, 59, 0, timezone.utc))
'2009-01-01T12:59:59Z'
The timestamp be normalized to UTC unless :python:`utc=False` is specified, in which case
it will use the timezone from the :class:`~datetime.datetime`'s :attr:`~datetime.datetime.tzinfo` attribute.
>>> eastern = ZoneInfo('US/Eastern')
>>> dt = datetime(2009, 1, 1, 12, 59, 59, tzinfo=eastern)
>>> generate(dt)
'2009-01-01T17:59:59Z'
>>> generate(dt, utc=False)
'2009-01-01T12:59:59-05:00'
Unless :python:`accept_naive=True` is specified, the :class:`~datetime.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, however, :python:`accept_naive=True` is specified, the :class:`~datetime.datetime` is assumed to represent a UTC time.
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
:param datetime.datetime dt: the :class:`~datetime.datetime` for which to generate an :RFC:`3339` timestamp.
:param bool utc: :const:`True` to normalize the supplied :class:`datetime.datetime` to UTC; :const:`False` otherwise.
Defaults to :const:`True`.
:param bool accept_naive: :const:`True` if :func:`generate()` should accept a 'naive' datetime
(that is, one without timezone information) and treat it as a UTC timestamp;
:const:`False` otherwise. Defaults to :const:`False`.
:param bool microseconds: :const:`True` to generate a timestamp which includes fractional seconds, if present;
:const:`False` otherwise. Defaults to :const:`False`.
Note that fractional seconds are *truncated*,
not rounded when :obj:`microseconds` is :const:`False`.
:return: the supplied :class:`~datetime.datetime` instance represented as an :RFC:`3339` timestamp
:rtype: str
"""
if dt.tzinfo is None:
if accept_naive:
if utc:
dt = dt.replace(tzinfo=timezone.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:
dt = dt.astimezone(timezone.utc)
timestamp = dt.isoformat(timespec="microseconds" if microseconds else "seconds")
if dt.tzinfo == timezone.utc:
timestamp = re.sub(r"\+00:00$", "Z", timestamp)
return timestamp
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1755966304.0
pyrfc3339-2.1.0/pyrfc3339/parser.py 0000644 0001751 0000166 00000006507 15052365540 016112 0 ustar 00runner docker import re
import sys
from datetime import datetime, timezone
from .utils import datetime_utcoffset
def parse(timestamp: str, utc: bool = False, produce_naive: bool = False) -> datetime:
"""
Parse an :RFC:`3339`-formatted timestamp and return a :class:`datetime.datetime`.
If the timestamp is presented in UTC, then the :attr:`~datetime.datetime.tzinfo` attribute of the
returned `datetime` will be set to :attr:`datetime.timezone.utc`.
>>> parse('2009-01-01T10:01:02Z')
datetime.datetime(2009, 1, 1, 10, 1, 2, tzinfo=datetime.timezone.utc)
Otherwise, a :class:`datetime.timezone` instance is created with the appropriate offset, and
the :attr:`~datetime.datetime.tzinfo` attribute of the returned :class:`~datetime.datetime` is set to that value.
>>> parse('2009-01-01T14:01:02-04:00')
datetime.datetime(2009, 1, 1, 14, 1, 2, tzinfo=datetime.timezone(datetime.timedelta(days=-1, seconds=72000)))
However, if :meth:`parse()` is called with :python:`utc=True`, then the returned
:class:`~datetime.datetime` will be normalized to UTC (and its :attr:`~datetime.datetime.tzinfo` attribute set to
:attr:`~datetime.timezone.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=datetime.timezone.utc)
As parsing is delegated to :meth:`datetime.datetime.fromisoformat()`, certain
timestamps which do not strictly adhere to :RFC:`3339` are nonetheless accepted.
>>> parse('2009-01-01T06:01:02')
datetime.datetime(2009, 1, 1, 6, 1, 2)
Exceptions will, however, be thrown for blatantly invalid input:
>>> parse('2009-01-01T25:01:02Z')
Traceback (most recent call last):
...
ValueError: hour must be in 0..23
:param str timestamp: the :RFC:`3339` timestamp to be parsed
:param bool utc: :const:`True` to normalize the timestamp to UTC; :const:`False` otherwise. Defaults to :const:`False`.
:param bool produce_naive: :const:`True` if the produced :class:`~datetime.datetime` instance should
not have a timezone attached (that is, be 'naive'); :const:`False` otherwise.
Defaults to :const:`False`.
:return: the parsed timestamp
:rtype: datetime.datetime
"""
# Python does not recognize "Z" as an alias for "+00:00", so we perform the
# substitution here.
timestamp = re.sub("Z$", "+00:00", timestamp, flags=re.IGNORECASE)
# Python releases prior to 3.11 only support three or six digits of fractional
# seconds. RFC 3339 is more lenient, so pad to six digits and truncate any
# excessive digits.
# This can be removed in October 2026, once Python 3.10 and earlier
# have been retired.
# noinspection PyUnreachableCode
if sys.version_info < (3, 11):
timestamp = re.sub(
r"(\.)([0-9]+)(?=[+\-][0-9]{2}:[0-9]{2}$)",
lambda match: match.group(1) + match.group(2).ljust(6, "0")[:6],
timestamp,
)
dt_out = datetime.fromisoformat(timestamp)
if utc:
dt_out = dt_out.astimezone(timezone.utc)
if produce_naive:
if datetime_utcoffset(dt_out) == 0:
dt_out = dt_out.replace(tzinfo=None)
else:
raise ValueError("cannot produce a naive datetime from a local timestamp")
return dt_out
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1755966304.0
pyrfc3339-2.1.0/pyrfc3339/py.typed 0000644 0001751 0000166 00000000000 15052365540 015721 0 ustar 00runner docker ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1755966304.0
pyrfc3339-2.1.0/pyrfc3339/utils.py 0000644 0001751 0000166 00000001607 15052365540 015752 0 ustar 00runner docker from datetime import datetime
def datetime_utcoffset(dt: datetime) -> float:
"""
Return the UTC offset for an aware :class:`datetime.datetime` in seconds.
>>> from datetime import datetime
>>> from zoneinfo import ZoneInfo
>>> z = ZoneInfo('US/Eastern')
>>> dt = datetime(2024, 11, 5, 19, 7, 6, tzinfo=z)
>>> datetime_utcoffset(dt)
-18000.0
>>> dt = datetime(2024, 11, 5, 19, 7, 6)
>>> datetime_utcoffset(dt)
Traceback (most recent call last):
...
AssertionError
:param datetime.datetime dt: a :class:`~datetime.datetime` instance; must be aware (that is, have a timezone attached)
:return: the UTC offset of the supplied :class:`~datetime.datetime` in seconds
:rtype: float
"""
assert dt.tzinfo is not None
tz = dt.tzinfo
offset = tz.utcoffset(dt)
assert offset is not None
return offset.total_seconds()
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1755966309.4669907
pyrfc3339-2.1.0/setup.cfg 0000644 0001751 0000166 00000000046 15052365545 014415 0 ustar 00runner docker [egg_info]
tag_build =
tag_date = 0