moved ftrack module one hierarchy level higher

This commit is contained in:
Jakub Trllo 2022-02-22 15:18:12 +01:00
parent ad6ab7934e
commit 4306a618a0
325 changed files with 41913 additions and 8 deletions

6
.gitmodules vendored
View file

@ -4,9 +4,3 @@
[submodule "repos/avalon-unreal-integration"]
path = repos/avalon-unreal-integration
url = https://github.com/pypeclub/avalon-unreal-integration.git
[submodule "openpype/modules/default_modules/ftrack/python2_vendor/arrow"]
path = openpype/modules/default_modules/ftrack/python2_vendor/arrow
url = https://github.com/arrow-py/arrow.git
[submodule "openpype/modules/default_modules/ftrack/python2_vendor/ftrack-python-api"]
path = openpype/modules/default_modules/ftrack/python2_vendor/ftrack-python-api
url = https://bitbucket.org/ftrack/ftrack-python-api.git

View file

@ -35,6 +35,7 @@ DEFAULT_OPENPYPE_MODULES = (
"log_viewer",
"muster",
"python_console_interpreter",
"ftrack",
"slack",
"webserver",
"launcher_action",

@ -1 +0,0 @@
Subproject commit b746fedf7286c3755a46f07ab72f4c414cd41fc0

@ -1 +0,0 @@
Subproject commit d277f474ab016e7b53479c36af87cb861d0cc53e

View file

@ -0,0 +1,211 @@
README.rst.new
# Small entry point file for debugging tasks
test.py
# 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/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
# 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/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
.python-version
# celery beat schedule file
celerybeat-schedule
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
local/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# Swap
[._]*.s[a-v][a-z]
[._]*.sw[a-p]
[._]s[a-rt-v][a-z]
[._]ss[a-gi-z]
[._]sw[a-p]
# Session
Session.vim
Sessionx.vim
# Temporary
.netrwhist
*~
# Auto-generated tag files
tags
# Persistent undo
[._]*.un~
.idea/
.vscode/
# 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
*~
# temporary files which can be created if a process still has a handle open of a deleted file
.fuse_hidden*
# KDE directory preferences
.directory
# Linux trash folder which might appear on any partition or disk
.Trash-*
# .nfs files are created when an open file is removed but is still being accessed
.nfs*
# Windows thumbnail cache files
Thumbs.db
Thumbs.db:encryptable
ehthumbs.db
ehthumbs_vista.db
# Dump file
*.stackdump
# Folder config file
[Dd]esktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msix
*.msm
*.msp
# Windows shortcuts
*.lnk

View file

@ -0,0 +1,41 @@
default_language_version:
python: python3
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v3.2.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: fix-encoding-pragma
exclude: ^arrow/_version.py
- id: requirements-txt-fixer
- id: check-ast
- id: check-yaml
- id: check-case-conflict
- id: check-docstring-first
- id: check-merge-conflict
- id: debug-statements
- repo: https://github.com/timothycrosley/isort
rev: 5.4.2
hooks:
- id: isort
- repo: https://github.com/asottile/pyupgrade
rev: v2.7.2
hooks:
- id: pyupgrade
- repo: https://github.com/pre-commit/pygrep-hooks
rev: v1.6.0
hooks:
- id: python-no-eval
- id: python-check-blanket-noqa
- id: rst-backticks
- repo: https://github.com/psf/black
rev: 20.8b1
hooks:
- id: black
args: [--safe, --quiet]
- repo: https://gitlab.com/pycqa/flake8
rev: 3.8.3
hooks:
- id: flake8
additional_dependencies: [flake8-bugbear]

View file

@ -0,0 +1,598 @@
Changelog
=========
0.17.0 (2020-10-2)
-------------------
- [WARN] Arrow will **drop support** for Python 2.7 and 3.5 in the upcoming 1.0.0 release. This is the last major release to support Python 2.7 and Python 3.5.
- [NEW] Arrow now properly handles imaginary datetimes during DST shifts. For example:
..code-block:: python
>>> just_before = arrow.get(2013, 3, 31, 1, 55, tzinfo="Europe/Paris")
>>> just_before.shift(minutes=+10)
<Arrow [2013-03-31T03:05:00+02:00]>
..code-block:: python
>>> before = arrow.get("2018-03-10 23:00:00", "YYYY-MM-DD HH:mm:ss", tzinfo="US/Pacific")
>>> after = arrow.get("2018-03-11 04:00:00", "YYYY-MM-DD HH:mm:ss", tzinfo="US/Pacific")
>>> result=[(t, t.to("utc")) for t in arrow.Arrow.range("hour", before, after)]
>>> for r in result:
... print(r)
...
(<Arrow [2018-03-10T23:00:00-08:00]>, <Arrow [2018-03-11T07:00:00+00:00]>)
(<Arrow [2018-03-11T00:00:00-08:00]>, <Arrow [2018-03-11T08:00:00+00:00]>)
(<Arrow [2018-03-11T01:00:00-08:00]>, <Arrow [2018-03-11T09:00:00+00:00]>)
(<Arrow [2018-03-11T03:00:00-07:00]>, <Arrow [2018-03-11T10:00:00+00:00]>)
(<Arrow [2018-03-11T04:00:00-07:00]>, <Arrow [2018-03-11T11:00:00+00:00]>)
- [NEW] Added ``humanize`` week granularity translation for Tagalog.
- [CHANGE] Calls to the ``timestamp`` property now emit a ``DeprecationWarning``. In a future release, ``timestamp`` will be changed to a method to align with Python's datetime module. If you would like to continue using the property, please change your code to use the ``int_timestamp`` or ``float_timestamp`` properties instead.
- [CHANGE] Expanded and improved Catalan locale.
- [FIX] Fixed a bug that caused ``Arrow.range()`` to incorrectly cut off ranges in certain scenarios when using month, quarter, or year endings.
- [FIX] Fixed a bug that caused day of week token parsing to be case sensitive.
- [INTERNAL] A number of functions were reordered in arrow.py for better organization and grouping of related methods. This change will have no impact on usage.
- [INTERNAL] A minimum tox version is now enforced for compatibility reasons. Contributors must use tox >3.18.0 going forward.
0.16.0 (2020-08-23)
-------------------
- [WARN] Arrow will **drop support** for Python 2.7 and 3.5 in the upcoming 1.0.0 release. The 0.16.x and 0.17.x releases are the last to support Python 2.7 and 3.5.
- [NEW] Implemented `PEP 495 <https://www.python.org/dev/peps/pep-0495/>`_ to handle ambiguous datetimes. This is achieved by the addition of the ``fold`` attribute for Arrow objects. For example:
.. code-block:: python
>>> before = Arrow(2017, 10, 29, 2, 0, tzinfo='Europe/Stockholm')
<Arrow [2017-10-29T02:00:00+02:00]>
>>> before.fold
0
>>> before.ambiguous
True
>>> after = Arrow(2017, 10, 29, 2, 0, tzinfo='Europe/Stockholm', fold=1)
<Arrow [2017-10-29T02:00:00+01:00]>
>>> after = before.replace(fold=1)
<Arrow [2017-10-29T02:00:00+01:00]>
- [NEW] Added ``normalize_whitespace`` flag to ``arrow.get``. This is useful for parsing log files and/or any files that may contain inconsistent spacing. For example:
.. code-block:: python
>>> arrow.get("Jun 1 2005 1:33PM", "MMM D YYYY H:mmA", normalize_whitespace=True)
<Arrow [2005-06-01T13:33:00+00:00]>
>>> arrow.get("2013-036 \t 04:05:06Z", normalize_whitespace=True)
<Arrow [2013-02-05T04:05:06+00:00]>
0.15.8 (2020-07-23)
-------------------
- [WARN] Arrow will **drop support** for Python 2.7 and 3.5 in the upcoming 1.0.0 release. The 0.15.x, 0.16.x, and 0.17.x releases are the last to support Python 2.7 and 3.5.
- [NEW] Added ``humanize`` week granularity translation for Czech.
- [FIX] ``arrow.get`` will now pick sane defaults when weekdays are passed with particular token combinations, see `#446 <https://github.com/arrow-py/arrow/issues/446>`_.
- [INTERNAL] Moved arrow to an organization. The repo can now be found `here <https://github.com/arrow-py/arrow>`_.
- [INTERNAL] Started issuing deprecation warnings for Python 2.7 and 3.5.
- [INTERNAL] Added Python 3.9 to CI pipeline.
0.15.7 (2020-06-19)
-------------------
- [NEW] Added a number of built-in format strings. See the `docs <https://arrow.readthedocs.io/#built-in-formats>`_ for a complete list of supported formats. For example:
.. code-block:: python
>>> arw = arrow.utcnow()
>>> arw.format(arrow.FORMAT_COOKIE)
'Wednesday, 27-May-2020 10:30:35 UTC'
- [NEW] Arrow is now fully compatible with Python 3.9 and PyPy3.
- [NEW] Added Makefile, tox.ini, and requirements.txt files to the distribution bundle.
- [NEW] Added French Canadian and Swahili locales.
- [NEW] Added ``humanize`` week granularity translation for Hebrew, Greek, Macedonian, Swedish, Slovak.
- [FIX] ms and μs timestamps are now normalized in ``arrow.get()``, ``arrow.fromtimestamp()``, and ``arrow.utcfromtimestamp()``. For example:
.. code-block:: python
>>> ts = 1591161115194556
>>> arw = arrow.get(ts)
<Arrow [2020-06-03T05:11:55.194556+00:00]>
>>> arw.timestamp
1591161115
- [FIX] Refactored and updated Macedonian, Hebrew, Korean, and Portuguese locales.
0.15.6 (2020-04-29)
-------------------
- [NEW] Added support for parsing and formatting `ISO 8601 week dates <https://en.wikipedia.org/wiki/ISO_week_date>`_ via a new token ``W``, for example:
.. code-block:: python
>>> arrow.get("2013-W29-6", "W")
<Arrow [2013-07-20T00:00:00+00:00]>
>>> utc=arrow.utcnow()
>>> utc
<Arrow [2020-01-23T18:37:55.417624+00:00]>
>>> utc.format("W")
'2020-W04-4'
- [NEW] Formatting with ``x`` token (microseconds) is now possible, for example:
.. code-block:: python
>>> dt = arrow.utcnow()
>>> dt.format("x")
'1585669870688329'
>>> dt.format("X")
'1585669870'
- [NEW] Added ``humanize`` week granularity translation for German, Italian, Polish & Taiwanese locales.
- [FIX] Consolidated and simplified German locales.
- [INTERNAL] Moved testing suite from nosetest/Chai to pytest/pytest-mock.
- [INTERNAL] Converted xunit-style setup and teardown functions in tests to pytest fixtures.
- [INTERNAL] Setup Github Actions for CI alongside Travis.
- [INTERNAL] Help support Arrow's future development by donating to the project on `Open Collective <https://opencollective.com/arrow>`_.
0.15.5 (2020-01-03)
-------------------
- [WARN] Python 2 reached EOL on 2020-01-01. arrow will **drop support** for Python 2 in a future release to be decided (see `#739 <https://github.com/arrow-py/arrow/issues/739>`_).
- [NEW] Added bounds parameter to ``span_range``, ``interval`` and ``span`` methods. This allows you to include or exclude the start and end values.
- [NEW] ``arrow.get()`` can now create arrow objects from a timestamp with a timezone, for example:
.. code-block:: python
>>> arrow.get(1367900664, tzinfo=tz.gettz('US/Pacific'))
<Arrow [2013-05-06T21:24:24-07:00]>
- [NEW] ``humanize`` can now combine multiple levels of granularity, for example:
.. code-block:: python
>>> later140 = arrow.utcnow().shift(seconds=+8400)
>>> later140.humanize(granularity="minute")
'in 139 minutes'
>>> later140.humanize(granularity=["hour", "minute"])
'in 2 hours and 19 minutes'
- [NEW] Added Hong Kong locale (``zh_hk``).
- [NEW] Added ``humanize`` week granularity translation for Dutch.
- [NEW] Numbers are now displayed when using the seconds granularity in ``humanize``.
- [CHANGE] ``range`` now supports both the singular and plural forms of the ``frames`` argument (e.g. day and days).
- [FIX] Improved parsing of strings that contain punctuation.
- [FIX] Improved behaviour of ``humanize`` when singular seconds are involved.
0.15.4 (2019-11-02)
-------------------
- [FIX] Fixed an issue that caused package installs to fail on Conda Forge.
0.15.3 (2019-11-02)
-------------------
- [NEW] ``factory.get()`` can now create arrow objects from a ISO calendar tuple, for example:
.. code-block:: python
>>> arrow.get((2013, 18, 7))
<Arrow [2013-05-05T00:00:00+00:00]>
- [NEW] Added a new token ``x`` to allow parsing of integer timestamps with milliseconds and microseconds.
- [NEW] Formatting now supports escaping of characters using the same syntax as parsing, for example:
.. code-block:: python
>>> arw = arrow.now()
>>> fmt = "YYYY-MM-DD h [h] m"
>>> arw.format(fmt)
'2019-11-02 3 h 32'
- [NEW] Added ``humanize`` week granularity translations for Chinese, Spanish and Vietnamese.
- [CHANGE] Added ``ParserError`` to module exports.
- [FIX] Added support for midnight at end of day. See `#703 <https://github.com/arrow-py/arrow/issues/703>`_ for details.
- [INTERNAL] Created Travis build for macOS.
- [INTERNAL] Test parsing and formatting against full timezone database.
0.15.2 (2019-09-14)
-------------------
- [NEW] Added ``humanize`` week granularity translations for Portuguese and Brazilian Portuguese.
- [NEW] Embedded changelog within docs and added release dates to versions.
- [FIX] Fixed a bug that caused test failures on Windows only, see `#668 <https://github.com/arrow-py/arrow/issues/668>`_ for details.
0.15.1 (2019-09-10)
-------------------
- [NEW] Added ``humanize`` week granularity translations for Japanese.
- [FIX] Fixed a bug that caused Arrow to fail when passed a negative timestamp string.
- [FIX] Fixed a bug that caused Arrow to fail when passed a datetime object with ``tzinfo`` of type ``StaticTzInfo``.
0.15.0 (2019-09-08)
-------------------
- [NEW] Added support for DDD and DDDD ordinal date tokens. The following functionality is now possible: ``arrow.get("1998-045")``, ``arrow.get("1998-45", "YYYY-DDD")``, ``arrow.get("1998-045", "YYYY-DDDD")``.
- [NEW] ISO 8601 basic format for dates and times is now supported (e.g. ``YYYYMMDDTHHmmssZ``).
- [NEW] Added ``humanize`` week granularity translations for French, Russian and Swiss German locales.
- [CHANGE] Timestamps of type ``str`` are no longer supported **without a format string** in the ``arrow.get()`` method. This change was made to support the ISO 8601 basic format and to address bugs such as `#447 <https://github.com/arrow-py/arrow/issues/447>`_.
The following will NOT work in v0.15.0:
.. code-block:: python
>>> arrow.get("1565358758")
>>> arrow.get("1565358758.123413")
The following will work in v0.15.0:
.. code-block:: python
>>> arrow.get("1565358758", "X")
>>> arrow.get("1565358758.123413", "X")
>>> arrow.get(1565358758)
>>> arrow.get(1565358758.123413)
- [CHANGE] When a meridian token (a|A) is passed and no meridians are available for the specified locale (e.g. unsupported or untranslated) a ``ParserError`` is raised.
- [CHANGE] The timestamp token (``X``) will now match float timestamps of type ``str``: ``arrow.get(“1565358758.123415”, “X”)``.
- [CHANGE] Strings with leading and/or trailing whitespace will no longer be parsed without a format string. Please see `the docs <https://arrow.readthedocs.io/#regular-expressions>`_ for ways to handle this.
- [FIX] The timestamp token (``X``) will now only match on strings that **strictly contain integers and floats**, preventing incorrect matches.
- [FIX] Most instances of ``arrow.get()`` returning an incorrect ``Arrow`` object from a partial parsing match have been eliminated. The following issue have been addressed: `#91 <https://github.com/arrow-py/arrow/issues/91>`_, `#196 <https://github.com/arrow-py/arrow/issues/196>`_, `#396 <https://github.com/arrow-py/arrow/issues/396>`_, `#434 <https://github.com/arrow-py/arrow/issues/434>`_, `#447 <https://github.com/arrow-py/arrow/issues/447>`_, `#456 <https://github.com/arrow-py/arrow/issues/456>`_, `#519 <https://github.com/arrow-py/arrow/issues/519>`_, `#538 <https://github.com/arrow-py/arrow/issues/538>`_, `#560 <https://github.com/arrow-py/arrow/issues/560>`_.
0.14.7 (2019-09-04)
-------------------
- [CHANGE] ``ArrowParseWarning`` will no longer be printed on every call to ``arrow.get()`` with a datetime string. The purpose of the warning was to start a conversation about the upcoming 0.15.0 changes and we appreciate all the feedback that the community has given us!
0.14.6 (2019-08-28)
-------------------
- [NEW] Added support for ``week`` granularity in ``Arrow.humanize()``. For example, ``arrow.utcnow().shift(weeks=-1).humanize(granularity="week")`` outputs "a week ago". This change introduced two new untranslated words, ``week`` and ``weeks``, to all locale dictionaries, so locale contributions are welcome!
- [NEW] Fully translated the Brazilian Portugese locale.
- [CHANGE] Updated the Macedonian locale to inherit from a Slavic base.
- [FIX] Fixed a bug that caused ``arrow.get()`` to ignore tzinfo arguments of type string (e.g. ``arrow.get(tzinfo="Europe/Paris")``).
- [FIX] Fixed a bug that occurred when ``arrow.Arrow()`` was instantiated with a ``pytz`` tzinfo object.
- [FIX] Fixed a bug that caused Arrow to fail when passed a sub-second token, that when rounded, had a value greater than 999999 (e.g. ``arrow.get("2015-01-12T01:13:15.9999995")``). Arrow should now accurately propagate the rounding for large sub-second tokens.
0.14.5 (2019-08-09)
-------------------
- [NEW] Added Afrikaans locale.
- [CHANGE] Removed deprecated ``replace`` shift functionality. Users looking to pass plural properties to the ``replace`` function to shift values should use ``shift`` instead.
- [FIX] Fixed bug that occurred when ``factory.get()`` was passed a locale kwarg.
0.14.4 (2019-07-30)
-------------------
- [FIX] Fixed a regression in 0.14.3 that prevented a tzinfo argument of type string to be passed to the ``get()`` function. Functionality such as ``arrow.get("2019072807", "YYYYMMDDHH", tzinfo="UTC")`` should work as normal again.
- [CHANGE] Moved ``backports.functools_lru_cache`` dependency from ``extra_requires`` to ``install_requires`` for ``Python 2.7`` installs to fix `#495 <https://github.com/arrow-py/arrow/issues/495>`_.
0.14.3 (2019-07-28)
-------------------
- [NEW] Added full support for Python 3.8.
- [CHANGE] Added warnings for upcoming factory.get() parsing changes in 0.15.0. Please see `#612 <https://github.com/arrow-py/arrow/issues/612>`_ for full details.
- [FIX] Extensive refactor and update of documentation.
- [FIX] factory.get() can now construct from kwargs.
- [FIX] Added meridians to Spanish Locale.
0.14.2 (2019-06-06)
-------------------
- [CHANGE] Travis CI builds now use tox to lint and run tests.
- [FIX] Fixed UnicodeDecodeError on certain locales (#600).
0.14.1 (2019-06-06)
-------------------
- [FIX] Fixed ``ImportError: No module named 'dateutil'`` (#598).
0.14.0 (2019-06-06)
-------------------
- [NEW] Added provisional support for Python 3.8.
- [CHANGE] Removed support for EOL Python 3.4.
- [FIX] Updated setup.py with modern Python standards.
- [FIX] Upgraded dependencies to latest versions.
- [FIX] Enabled flake8 and black on travis builds.
- [FIX] Formatted code using black and isort.
0.13.2 (2019-05-30)
-------------------
- [NEW] Add is_between method.
- [FIX] Improved humanize behaviour for near zero durations (#416).
- [FIX] Correct humanize behaviour with future days (#541).
- [FIX] Documentation updates.
- [FIX] Improvements to German Locale.
0.13.1 (2019-02-17)
-------------------
- [NEW] Add support for Python 3.7.
- [CHANGE] Remove deprecation decorators for Arrow.range(), Arrow.span_range() and Arrow.interval(), all now return generators, wrap with list() to get old behavior.
- [FIX] Documentation and docstring updates.
0.13.0 (2019-01-09)
-------------------
- [NEW] Added support for Python 3.6.
- [CHANGE] Drop support for Python 2.6/3.3.
- [CHANGE] Return generator instead of list for Arrow.range(), Arrow.span_range() and Arrow.interval().
- [FIX] Make arrow.get() work with str & tzinfo combo.
- [FIX] Make sure special RegEx characters are escaped in format string.
- [NEW] Added support for ZZZ when formatting.
- [FIX] Stop using datetime.utcnow() in internals, use datetime.now(UTC) instead.
- [FIX] Return NotImplemented instead of TypeError in arrow math internals.
- [NEW] Added Estonian Locale.
- [FIX] Small fixes to Greek locale.
- [FIX] TagalogLocale improvements.
- [FIX] Added test requirements to setup.
- [FIX] Improve docs for get, now and utcnow methods.
- [FIX] Correct typo in depreciation warning.
0.12.1
------
- [FIX] Allow universal wheels to be generated and reliably installed.
- [FIX] Make humanize respect only_distance when granularity argument is also given.
0.12.0
------
- [FIX] Compatibility fix for Python 2.x
0.11.0
------
- [FIX] Fix grammar of ArabicLocale
- [NEW] Add Nepali Locale
- [FIX] Fix month name + rename AustriaLocale -> AustrianLocale
- [FIX] Fix typo in Basque Locale
- [FIX] Fix grammar in PortugueseBrazilian locale
- [FIX] Remove pip --user-mirrors flag
- [NEW] Add Indonesian Locale
0.10.0
------
- [FIX] Fix getattr off by one for quarter
- [FIX] Fix negative offset for UTC
- [FIX] Update arrow.py
0.9.0
-----
- [NEW] Remove duplicate code
- [NEW] Support gnu date iso 8601
- [NEW] Add support for universal wheels
- [NEW] Slovenian locale
- [NEW] Slovak locale
- [NEW] Romanian locale
- [FIX] respect limit even if end is defined range
- [FIX] Separate replace & shift functions
- [NEW] Added tox
- [FIX] Fix supported Python versions in documentation
- [NEW] Azerbaijani locale added, locale issue fixed in Turkish.
- [FIX] Format ParserError's raise message
0.8.0
-----
- []
0.7.1
-----
- [NEW] Esperanto locale (batisteo)
0.7.0
-----
- [FIX] Parse localized strings #228 (swistakm)
- [FIX] Modify tzinfo parameter in ``get`` api #221 (bottleimp)
- [FIX] Fix Czech locale (PrehistoricTeam)
- [FIX] Raise TypeError when adding/subtracting non-dates (itsmeolivia)
- [FIX] Fix pytz conversion error (Kudo)
- [FIX] Fix overzealous time truncation in span_range (kdeldycke)
- [NEW] Humanize for time duration #232 (ybrs)
- [NEW] Add Thai locale (sipp11)
- [NEW] Adding Belarusian (be) locale (oire)
- [NEW] Search date in strings (beenje)
- [NEW] Note that arrow's tokens differ from strptime's. (offby1)
0.6.0
-----
- [FIX] Added support for Python 3
- [FIX] Avoid truncating oversized epoch timestamps. Fixes #216.
- [FIX] Fixed month abbreviations for Ukrainian
- [FIX] Fix typo timezone
- [FIX] A couple of dialect fixes and two new languages
- [FIX] Spanish locale: ``Miercoles`` should have acute accent
- [Fix] Fix Finnish grammar
- [FIX] Fix typo in 'Arrow.floor' docstring
- [FIX] Use read() utility to open README
- [FIX] span_range for week frame
- [NEW] Add minimal support for fractional seconds longer than six digits.
- [NEW] Adding locale support for Marathi (mr)
- [NEW] Add count argument to span method
- [NEW] Improved docs
0.5.1 - 0.5.4
-------------
- [FIX] test the behavior of simplejson instead of calling for_json directly (tonyseek)
- [FIX] Add Hebrew Locale (doodyparizada)
- [FIX] Update documentation location (andrewelkins)
- [FIX] Update setup.py Development Status level (andrewelkins)
- [FIX] Case insensitive month match (cshowe)
0.5.0
-----
- [NEW] struct_time addition. (mhworth)
- [NEW] Version grep (eirnym)
- [NEW] Default to ISO 8601 format (emonty)
- [NEW] Raise TypeError on comparison (sniekamp)
- [NEW] Adding Macedonian(mk) locale (krisfremen)
- [FIX] Fix for ISO seconds and fractional seconds (sdispater) (andrewelkins)
- [FIX] Use correct Dutch wording for "hours" (wbolster)
- [FIX] Complete the list of english locales (indorilftw)
- [FIX] Change README to reStructuredText (nyuszika7h)
- [FIX] Parse lower-cased 'h' (tamentis)
- [FIX] Slight modifications to Dutch locale (nvie)
0.4.4
-----
- [NEW] Include the docs in the released tarball
- [NEW] Czech localization Czech localization for Arrow
- [NEW] Add fa_ir to locales
- [FIX] Fixes parsing of time strings with a final Z
- [FIX] Fixes ISO parsing and formatting for fractional seconds
- [FIX] test_fromtimestamp sp
- [FIX] some typos fixed
- [FIX] removed an unused import statement
- [FIX] docs table fix
- [FIX] Issue with specify 'X' template and no template at all to arrow.get
- [FIX] Fix "import" typo in docs/index.rst
- [FIX] Fix unit tests for zero passed
- [FIX] Update layout.html
- [FIX] In Norwegian and new Norwegian months and weekdays should not be capitalized
- [FIX] Fixed discrepancy between specifying 'X' to arrow.get and specifying no template
0.4.3
-----
- [NEW] Turkish locale (Emre)
- [NEW] Arabic locale (Mosab Ahmad)
- [NEW] Danish locale (Holmars)
- [NEW] Icelandic locale (Holmars)
- [NEW] Hindi locale (Atmb4u)
- [NEW] Malayalam locale (Atmb4u)
- [NEW] Finnish locale (Stormpat)
- [NEW] Portuguese locale (Danielcorreia)
- [NEW] ``h`` and ``hh`` strings are now supported (Averyonghub)
- [FIX] An incorrect inflection in the Polish locale has been fixed (Avalanchy)
- [FIX] ``arrow.get`` now properly handles ``Date`` (Jaapz)
- [FIX] Tests are now declared in ``setup.py`` and the manifest (Pypingou)
- [FIX] ``__version__`` has been added to ``__init__.py`` (Sametmax)
- [FIX] ISO 8601 strings can be parsed without a separator (Ivandiguisto / Root)
- [FIX] Documentation is now more clear regarding some inputs on ``arrow.get`` (Eriktaubeneck)
- [FIX] Some documentation links have been fixed (Vrutsky)
- [FIX] Error messages for parse errors are now more descriptive (Maciej Albin)
- [FIX] The parser now correctly checks for separators in strings (Mschwager)
0.4.2
-----
- [NEW] Factory ``get`` method now accepts a single ``Arrow`` argument.
- [NEW] Tokens SSSS, SSSSS and SSSSSS are supported in parsing.
- [NEW] ``Arrow`` objects have a ``float_timestamp`` property.
- [NEW] Vietnamese locale (Iu1nguoi)
- [NEW] Factory ``get`` method now accepts a list of format strings (Dgilland)
- [NEW] A MANIFEST.in file has been added (Pypingou)
- [NEW] Tests can be run directly from ``setup.py`` (Pypingou)
- [FIX] Arrow docs now list 'day of week' format tokens correctly (Rudolphfroger)
- [FIX] Several issues with the Korean locale have been resolved (Yoloseem)
- [FIX] ``humanize`` now correctly returns unicode (Shvechikov)
- [FIX] ``Arrow`` objects now pickle / unpickle correctly (Yoloseem)
0.4.1
-----
- [NEW] Table / explanation of formatting & parsing tokens in docs
- [NEW] Brazilian locale (Augusto2112)
- [NEW] Dutch locale (OrangeTux)
- [NEW] Italian locale (Pertux)
- [NEW] Austrain locale (LeChewbacca)
- [NEW] Tagalog locale (Marksteve)
- [FIX] Corrected spelling and day numbers in German locale (LeChewbacca)
- [FIX] Factory ``get`` method should now handle unicode strings correctly (Bwells)
- [FIX] Midnight and noon should now parse and format correctly (Bwells)
0.4.0
-----
- [NEW] Format-free ISO 8601 parsing in factory ``get`` method
- [NEW] Support for 'week' / 'weeks' in ``span``, ``range``, ``span_range``, ``floor`` and ``ceil``
- [NEW] Support for 'weeks' in ``replace``
- [NEW] Norwegian locale (Martinp)
- [NEW] Japanese locale (CortYuming)
- [FIX] Timezones no longer show the wrong sign when formatted (Bean)
- [FIX] Microseconds are parsed correctly from strings (Bsidhom)
- [FIX] Locale day-of-week is no longer off by one (Cynddl)
- [FIX] Corrected plurals of Ukrainian and Russian nouns (Catchagain)
- [CHANGE] Old 0.1 ``arrow`` module method removed
- [CHANGE] Dropped timestamp support in ``range`` and ``span_range`` (never worked correctly)
- [CHANGE] Dropped parsing of single string as tz string in factory ``get`` method (replaced by ISO 8601)
0.3.5
-----
- [NEW] French locale (Cynddl)
- [NEW] Spanish locale (Slapresta)
- [FIX] Ranges handle multiple timezones correctly (Ftobia)
0.3.4
-----
- [FIX] Humanize no longer sometimes returns the wrong month delta
- [FIX] ``__format__`` works correctly with no format string
0.3.3
-----
- [NEW] Python 2.6 support
- [NEW] Initial support for locale-based parsing and formatting
- [NEW] ArrowFactory class, now proxied as the module API
- [NEW] ``factory`` api method to obtain a factory for a custom type
- [FIX] Python 3 support and tests completely ironed out
0.3.2
-----
- [NEW] Python 3+ support
0.3.1
-----
- [FIX] The old ``arrow`` module function handles timestamps correctly as it used to
0.3.0
-----
- [NEW] ``Arrow.replace`` method
- [NEW] Accept timestamps, datetimes and Arrows for datetime inputs, where reasonable
- [FIX] ``range`` and ``span_range`` respect end and limit parameters correctly
- [CHANGE] Arrow objects are no longer mutable
- [CHANGE] Plural attribute name semantics altered: single -> absolute, plural -> relative
- [CHANGE] Plural names no longer supported as properties (e.g. ``arrow.utcnow().years``)
0.2.1
-----
- [NEW] Support for localized humanization
- [NEW] English, Russian, Greek, Korean, Chinese locales
0.2.0
-----
- **REWRITE**
- [NEW] Date parsing
- [NEW] Date formatting
- [NEW] ``floor``, ``ceil`` and ``span`` methods
- [NEW] ``datetime`` interface implementation
- [NEW] ``clone`` method
- [NEW] ``get``, ``now`` and ``utcnow`` API methods
0.1.6
-----
- [NEW] Humanized time deltas
- [NEW] ``__eq__`` implemented
- [FIX] Issues with conversions related to daylight savings time resolved
- [CHANGE] ``__str__`` uses ISO formatting
0.1.5
-----
- **Started tracking changes**
- [NEW] Parsing of ISO-formatted time zone offsets (e.g. '+02:30', '-05:00')
- [NEW] Resolved some issues with timestamps and delta / Olson time zones

View file

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2019 Chris Smith
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View file

@ -0,0 +1,3 @@
include LICENSE CHANGELOG.rst README.rst Makefile requirements.txt tox.ini
recursive-include tests *.py
recursive-include docs *.py *.rst *.bat Makefile

View file

@ -0,0 +1,44 @@
.PHONY: auto test docs clean
auto: build38
build27: PYTHON_VER = python2.7
build35: PYTHON_VER = python3.5
build36: PYTHON_VER = python3.6
build37: PYTHON_VER = python3.7
build38: PYTHON_VER = python3.8
build39: PYTHON_VER = python3.9
build27 build35 build36 build37 build38 build39: clean
virtualenv venv --python=$(PYTHON_VER)
. venv/bin/activate; \
pip install -r requirements.txt; \
pre-commit install
test:
rm -f .coverage coverage.xml
. venv/bin/activate; pytest
lint:
. venv/bin/activate; pre-commit run --all-files --show-diff-on-failure
docs:
rm -rf docs/_build
. venv/bin/activate; cd docs; make html
clean: clean-dist
rm -rf venv .pytest_cache ./**/__pycache__
rm -f .coverage coverage.xml ./**/*.pyc
clean-dist:
rm -rf dist build .egg .eggs arrow.egg-info
build-dist:
. venv/bin/activate; \
pip install -U setuptools twine wheel; \
python setup.py sdist bdist_wheel
upload-dist:
. venv/bin/activate; twine upload dist/*
publish: test clean-dist build-dist upload-dist clean-dist

View file

@ -0,0 +1,133 @@
Arrow: Better dates & times for Python
======================================
.. start-inclusion-marker-do-not-remove
.. image:: https://github.com/arrow-py/arrow/workflows/tests/badge.svg?branch=master
:alt: Build Status
:target: https://github.com/arrow-py/arrow/actions?query=workflow%3Atests+branch%3Amaster
.. image:: https://codecov.io/gh/arrow-py/arrow/branch/master/graph/badge.svg
:alt: Coverage
:target: https://codecov.io/gh/arrow-py/arrow
.. image:: https://img.shields.io/pypi/v/arrow.svg
:alt: PyPI Version
:target: https://pypi.python.org/pypi/arrow
.. image:: https://img.shields.io/pypi/pyversions/arrow.svg
:alt: Supported Python Versions
:target: https://pypi.python.org/pypi/arrow
.. image:: https://img.shields.io/pypi/l/arrow.svg
:alt: License
:target: https://pypi.python.org/pypi/arrow
.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
:alt: Code Style: Black
:target: https://github.com/psf/black
**Arrow** is a Python library that offers a sensible and human-friendly approach to creating, manipulating, formatting and converting dates, times and timestamps. It implements and updates the datetime type, plugging gaps in functionality and providing an intelligent module API that supports many common creation scenarios. Simply put, it helps you work with dates and times with fewer imports and a lot less code.
Arrow is named after the `arrow of time <https://en.wikipedia.org/wiki/Arrow_of_time>`_ and is heavily inspired by `moment.js <https://github.com/moment/moment>`_ and `requests <https://github.com/psf/requests>`_.
Why use Arrow over built-in modules?
------------------------------------
Python's standard library and some other low-level modules have near-complete date, time and timezone functionality, but don't work very well from a usability perspective:
- Too many modules: datetime, time, calendar, dateutil, pytz and more
- Too many types: date, time, datetime, tzinfo, timedelta, relativedelta, etc.
- Timezones and timestamp conversions are verbose and unpleasant
- Timezone naivety is the norm
- Gaps in functionality: ISO 8601 parsing, timespans, humanization
Features
--------
- Fully-implemented, drop-in replacement for datetime
- Supports Python 2.7, 3.5, 3.6, 3.7, 3.8 and 3.9
- Timezone-aware and UTC by default
- Provides super-simple creation options for many common input scenarios
- :code:`shift` method with support for relative offsets, including weeks
- Formats and parses strings automatically
- Wide support for ISO 8601
- Timezone conversion
- Timestamp available as a property
- Generates time spans, ranges, floors and ceilings for time frames ranging from microsecond to year
- Humanizes and supports a growing list of contributed locales
- Extensible for your own Arrow-derived types
Quick Start
-----------
Installation
~~~~~~~~~~~~
To install Arrow, use `pip <https://pip.pypa.io/en/stable/quickstart/>`_ or `pipenv <https://docs.pipenv.org>`_:
.. code-block:: console
$ pip install -U arrow
Example Usage
~~~~~~~~~~~~~
.. code-block:: python
>>> import arrow
>>> arrow.get('2013-05-11T21:23:58.970460+07:00')
<Arrow [2013-05-11T21:23:58.970460+07:00]>
>>> utc = arrow.utcnow()
>>> utc
<Arrow [2013-05-11T21:23:58.970460+00:00]>
>>> utc = utc.shift(hours=-1)
>>> utc
<Arrow [2013-05-11T20:23:58.970460+00:00]>
>>> local = utc.to('US/Pacific')
>>> local
<Arrow [2013-05-11T13:23:58.970460-07:00]>
>>> local.timestamp
1368303838
>>> local.format()
'2013-05-11 13:23:58 -07:00'
>>> local.format('YYYY-MM-DD HH:mm:ss ZZ')
'2013-05-11 13:23:58 -07:00'
>>> local.humanize()
'an hour ago'
>>> local.humanize(locale='ko_kr')
'1시간 전'
.. end-inclusion-marker-do-not-remove
Documentation
-------------
For full documentation, please visit `arrow.readthedocs.io <https://arrow.readthedocs.io>`_.
Contributing
------------
Contributions are welcome for both code and localizations (adding and updating locales). Begin by gaining familiarity with the Arrow library and its features. Then, jump into contributing:
#. Find an issue or feature to tackle on the `issue tracker <https://github.com/arrow-py/arrow/issues>`_. Issues marked with the `"good first issue" label <https://github.com/arrow-py/arrow/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22>`_ may be a great place to start!
#. Fork `this repository <https://github.com/arrow-py/arrow>`_ on GitHub and begin making changes in a branch.
#. Add a few tests to ensure that the bug was fixed or the feature works as expected.
#. Run the entire test suite and linting checks by running one of the following commands: :code:`tox` (if you have `tox <https://tox.readthedocs.io>`_ installed) **OR** :code:`make build38 && make test && make lint` (if you do not have Python 3.8 installed, replace :code:`build38` with the latest Python version on your system).
#. Submit a pull request and await feedback 😃.
If you have any questions along the way, feel free to ask them `here <https://github.com/arrow-py/arrow/issues/new?labels=question>`_.
Support Arrow
-------------
`Open Collective <https://opencollective.com/>`_ is an online funding platform that provides tools to raise money and share your finances with full transparency. It is the platform of choice for individuals and companies to make one-time or recurring donations directly to the project. If you are interested in making a financial contribution, please visit the `Arrow collective <https://opencollective.com/arrow>`_.

View file

@ -0,0 +1,18 @@
# -*- coding: utf-8 -*-
from ._version import __version__
from .api import get, now, utcnow
from .arrow import Arrow
from .factory import ArrowFactory
from .formatter import (
FORMAT_ATOM,
FORMAT_COOKIE,
FORMAT_RFC822,
FORMAT_RFC850,
FORMAT_RFC1036,
FORMAT_RFC1123,
FORMAT_RFC2822,
FORMAT_RFC3339,
FORMAT_RSS,
FORMAT_W3C,
)
from .parser import ParserError

View file

@ -0,0 +1 @@
__version__ = "0.17.0"

View file

@ -0,0 +1,54 @@
# -*- coding: utf-8 -*-
"""
Provides the default implementation of :class:`ArrowFactory <arrow.factory.ArrowFactory>`
methods for use as a module API.
"""
from __future__ import absolute_import
from arrow.factory import ArrowFactory
# internal default factory.
_factory = ArrowFactory()
def get(*args, **kwargs):
"""Calls the default :class:`ArrowFactory <arrow.factory.ArrowFactory>` ``get`` method."""
return _factory.get(*args, **kwargs)
get.__doc__ = _factory.get.__doc__
def utcnow():
"""Calls the default :class:`ArrowFactory <arrow.factory.ArrowFactory>` ``utcnow`` method."""
return _factory.utcnow()
utcnow.__doc__ = _factory.utcnow.__doc__
def now(tz=None):
"""Calls the default :class:`ArrowFactory <arrow.factory.ArrowFactory>` ``now`` method."""
return _factory.now(tz)
now.__doc__ = _factory.now.__doc__
def factory(type):
"""Returns an :class:`.ArrowFactory` for the specified :class:`Arrow <arrow.arrow.Arrow>`
or derived type.
:param type: the type, :class:`Arrow <arrow.arrow.Arrow>` or derived.
"""
return ArrowFactory(type)
__all__ = ["get", "utcnow", "now", "factory"]

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,9 @@
# -*- coding: utf-8 -*-
# Output of time.mktime(datetime.max.timetuple()) on macOS
# This value must be hardcoded for compatibility with Windows
# Platform-independent max timestamps are hard to form
# https://stackoverflow.com/q/46133223
MAX_TIMESTAMP = 253402318799.0
MAX_TIMESTAMP_MS = MAX_TIMESTAMP * 1000
MAX_TIMESTAMP_US = MAX_TIMESTAMP * 1000000

View file

@ -0,0 +1,301 @@
# -*- coding: utf-8 -*-
"""
Implements the :class:`ArrowFactory <arrow.factory.ArrowFactory>` class,
providing factory methods for common :class:`Arrow <arrow.arrow.Arrow>`
construction scenarios.
"""
from __future__ import absolute_import
import calendar
from datetime import date, datetime
from datetime import tzinfo as dt_tzinfo
from time import struct_time
from dateutil import tz as dateutil_tz
from arrow import parser
from arrow.arrow import Arrow
from arrow.util import is_timestamp, iso_to_gregorian, isstr
class ArrowFactory(object):
"""A factory for generating :class:`Arrow <arrow.arrow.Arrow>` objects.
:param type: (optional) the :class:`Arrow <arrow.arrow.Arrow>`-based class to construct from.
Defaults to :class:`Arrow <arrow.arrow.Arrow>`.
"""
def __init__(self, type=Arrow):
self.type = type
def get(self, *args, **kwargs):
"""Returns an :class:`Arrow <arrow.arrow.Arrow>` object based on flexible inputs.
:param locale: (optional) a ``str`` specifying a locale for the parser. Defaults to 'en_us'.
:param tzinfo: (optional) a :ref:`timezone expression <tz-expr>` or tzinfo object.
Replaces the timezone unless using an input form that is explicitly UTC or specifies
the timezone in a positional argument. Defaults to UTC.
:param normalize_whitespace: (optional) a ``bool`` specifying whether or not to normalize
redundant whitespace (spaces, tabs, and newlines) in a datetime string before parsing.
Defaults to false.
Usage::
>>> import arrow
**No inputs** to get current UTC time::
>>> arrow.get()
<Arrow [2013-05-08T05:51:43.316458+00:00]>
**None** to also get current UTC time::
>>> arrow.get(None)
<Arrow [2013-05-08T05:51:49.016458+00:00]>
**One** :class:`Arrow <arrow.arrow.Arrow>` object, to get a copy.
>>> arw = arrow.utcnow()
>>> arrow.get(arw)
<Arrow [2013-10-23T15:21:54.354846+00:00]>
**One** ``float`` or ``int``, convertible to a floating-point timestamp, to get
that timestamp in UTC::
>>> arrow.get(1367992474.293378)
<Arrow [2013-05-08T05:54:34.293378+00:00]>
>>> arrow.get(1367992474)
<Arrow [2013-05-08T05:54:34+00:00]>
**One** ISO 8601-formatted ``str``, to parse it::
>>> arrow.get('2013-09-29T01:26:43.830580')
<Arrow [2013-09-29T01:26:43.830580+00:00]>
**One** ISO 8601-formatted ``str``, in basic format, to parse it::
>>> arrow.get('20160413T133656.456289')
<Arrow [2016-04-13T13:36:56.456289+00:00]>
**One** ``tzinfo``, to get the current time **converted** to that timezone::
>>> arrow.get(tz.tzlocal())
<Arrow [2013-05-07T22:57:28.484717-07:00]>
**One** naive ``datetime``, to get that datetime in UTC::
>>> arrow.get(datetime(2013, 5, 5))
<Arrow [2013-05-05T00:00:00+00:00]>
**One** aware ``datetime``, to get that datetime::
>>> arrow.get(datetime(2013, 5, 5, tzinfo=tz.tzlocal()))
<Arrow [2013-05-05T00:00:00-07:00]>
**One** naive ``date``, to get that date in UTC::
>>> arrow.get(date(2013, 5, 5))
<Arrow [2013-05-05T00:00:00+00:00]>
**One** time.struct time::
>>> arrow.get(gmtime(0))
<Arrow [1970-01-01T00:00:00+00:00]>
**One** iso calendar ``tuple``, to get that week date in UTC::
>>> arrow.get((2013, 18, 7))
<Arrow [2013-05-05T00:00:00+00:00]>
**Two** arguments, a naive or aware ``datetime``, and a replacement
:ref:`timezone expression <tz-expr>`::
>>> arrow.get(datetime(2013, 5, 5), 'US/Pacific')
<Arrow [2013-05-05T00:00:00-07:00]>
**Two** arguments, a naive ``date``, and a replacement
:ref:`timezone expression <tz-expr>`::
>>> arrow.get(date(2013, 5, 5), 'US/Pacific')
<Arrow [2013-05-05T00:00:00-07:00]>
**Two** arguments, both ``str``, to parse the first according to the format of the second::
>>> arrow.get('2013-05-05 12:30:45 America/Chicago', 'YYYY-MM-DD HH:mm:ss ZZZ')
<Arrow [2013-05-05T12:30:45-05:00]>
**Two** arguments, first a ``str`` to parse and second a ``list`` of formats to try::
>>> arrow.get('2013-05-05 12:30:45', ['MM/DD/YYYY', 'YYYY-MM-DD HH:mm:ss'])
<Arrow [2013-05-05T12:30:45+00:00]>
**Three or more** arguments, as for the constructor of a ``datetime``::
>>> arrow.get(2013, 5, 5, 12, 30, 45)
<Arrow [2013-05-05T12:30:45+00:00]>
"""
arg_count = len(args)
locale = kwargs.pop("locale", "en_us")
tz = kwargs.get("tzinfo", None)
normalize_whitespace = kwargs.pop("normalize_whitespace", False)
# if kwargs given, send to constructor unless only tzinfo provided
if len(kwargs) > 1:
arg_count = 3
# tzinfo kwarg is not provided
if len(kwargs) == 1 and tz is None:
arg_count = 3
# () -> now, @ utc.
if arg_count == 0:
if isstr(tz):
tz = parser.TzinfoParser.parse(tz)
return self.type.now(tz)
if isinstance(tz, dt_tzinfo):
return self.type.now(tz)
return self.type.utcnow()
if arg_count == 1:
arg = args[0]
# (None) -> now, @ utc.
if arg is None:
return self.type.utcnow()
# try (int, float) -> from timestamp with tz
elif not isstr(arg) and is_timestamp(arg):
if tz is None:
# set to UTC by default
tz = dateutil_tz.tzutc()
return self.type.fromtimestamp(arg, tzinfo=tz)
# (Arrow) -> from the object's datetime.
elif isinstance(arg, Arrow):
return self.type.fromdatetime(arg.datetime)
# (datetime) -> from datetime.
elif isinstance(arg, datetime):
return self.type.fromdatetime(arg)
# (date) -> from date.
elif isinstance(arg, date):
return self.type.fromdate(arg)
# (tzinfo) -> now, @ tzinfo.
elif isinstance(arg, dt_tzinfo):
return self.type.now(arg)
# (str) -> parse.
elif isstr(arg):
dt = parser.DateTimeParser(locale).parse_iso(arg, normalize_whitespace)
return self.type.fromdatetime(dt, tz)
# (struct_time) -> from struct_time
elif isinstance(arg, struct_time):
return self.type.utcfromtimestamp(calendar.timegm(arg))
# (iso calendar) -> convert then from date
elif isinstance(arg, tuple) and len(arg) == 3:
dt = iso_to_gregorian(*arg)
return self.type.fromdate(dt)
else:
raise TypeError(
"Can't parse single argument of type '{}'".format(type(arg))
)
elif arg_count == 2:
arg_1, arg_2 = args[0], args[1]
if isinstance(arg_1, datetime):
# (datetime, tzinfo/str) -> fromdatetime replace tzinfo.
if isinstance(arg_2, dt_tzinfo) or isstr(arg_2):
return self.type.fromdatetime(arg_1, arg_2)
else:
raise TypeError(
"Can't parse two arguments of types 'datetime', '{}'".format(
type(arg_2)
)
)
elif isinstance(arg_1, date):
# (date, tzinfo/str) -> fromdate replace tzinfo.
if isinstance(arg_2, dt_tzinfo) or isstr(arg_2):
return self.type.fromdate(arg_1, tzinfo=arg_2)
else:
raise TypeError(
"Can't parse two arguments of types 'date', '{}'".format(
type(arg_2)
)
)
# (str, format) -> parse.
elif isstr(arg_1) and (isstr(arg_2) or isinstance(arg_2, list)):
dt = parser.DateTimeParser(locale).parse(
args[0], args[1], normalize_whitespace
)
return self.type.fromdatetime(dt, tzinfo=tz)
else:
raise TypeError(
"Can't parse two arguments of types '{}' and '{}'".format(
type(arg_1), type(arg_2)
)
)
# 3+ args -> datetime-like via constructor.
else:
return self.type(*args, **kwargs)
def utcnow(self):
"""Returns an :class:`Arrow <arrow.arrow.Arrow>` object, representing "now" in UTC time.
Usage::
>>> import arrow
>>> arrow.utcnow()
<Arrow [2013-05-08T05:19:07.018993+00:00]>
"""
return self.type.utcnow()
def now(self, tz=None):
"""Returns an :class:`Arrow <arrow.arrow.Arrow>` object, representing "now" in the given
timezone.
:param tz: (optional) A :ref:`timezone expression <tz-expr>`. Defaults to local time.
Usage::
>>> import arrow
>>> arrow.now()
<Arrow [2013-05-07T22:19:11.363410-07:00]>
>>> arrow.now('US/Pacific')
<Arrow [2013-05-07T22:19:15.251821-07:00]>
>>> arrow.now('+02:00')
<Arrow [2013-05-08T07:19:25.618646+02:00]>
>>> arrow.now('local')
<Arrow [2013-05-07T22:19:39.130059-07:00]>
"""
if tz is None:
tz = dateutil_tz.tzlocal()
elif not isinstance(tz, dt_tzinfo):
tz = parser.TzinfoParser.parse(tz)
return self.type.now(tz)

View file

@ -0,0 +1,139 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, division
import calendar
import re
from dateutil import tz as dateutil_tz
from arrow import locales, util
FORMAT_ATOM = "YYYY-MM-DD HH:mm:ssZZ"
FORMAT_COOKIE = "dddd, DD-MMM-YYYY HH:mm:ss ZZZ"
FORMAT_RFC822 = "ddd, DD MMM YY HH:mm:ss Z"
FORMAT_RFC850 = "dddd, DD-MMM-YY HH:mm:ss ZZZ"
FORMAT_RFC1036 = "ddd, DD MMM YY HH:mm:ss Z"
FORMAT_RFC1123 = "ddd, DD MMM YYYY HH:mm:ss Z"
FORMAT_RFC2822 = "ddd, DD MMM YYYY HH:mm:ss Z"
FORMAT_RFC3339 = "YYYY-MM-DD HH:mm:ssZZ"
FORMAT_RSS = "ddd, DD MMM YYYY HH:mm:ss Z"
FORMAT_W3C = "YYYY-MM-DD HH:mm:ssZZ"
class DateTimeFormatter(object):
# This pattern matches characters enclosed in square brackets are matched as
# an atomic group. For more info on atomic groups and how to they are
# emulated in Python's re library, see https://stackoverflow.com/a/13577411/2701578
_FORMAT_RE = re.compile(
r"(\[(?:(?=(?P<literal>[^]]))(?P=literal))*\]|YYY?Y?|MM?M?M?|Do|DD?D?D?|d?dd?d?|HH?|hh?|mm?|ss?|SS?S?S?S?S?|ZZ?Z?|a|A|X|x|W)"
)
def __init__(self, locale="en_us"):
self.locale = locales.get_locale(locale)
def format(cls, dt, fmt):
return cls._FORMAT_RE.sub(lambda m: cls._format_token(dt, m.group(0)), fmt)
def _format_token(self, dt, token):
if token and token.startswith("[") and token.endswith("]"):
return token[1:-1]
if token == "YYYY":
return self.locale.year_full(dt.year)
if token == "YY":
return self.locale.year_abbreviation(dt.year)
if token == "MMMM":
return self.locale.month_name(dt.month)
if token == "MMM":
return self.locale.month_abbreviation(dt.month)
if token == "MM":
return "{:02d}".format(dt.month)
if token == "M":
return str(dt.month)
if token == "DDDD":
return "{:03d}".format(dt.timetuple().tm_yday)
if token == "DDD":
return str(dt.timetuple().tm_yday)
if token == "DD":
return "{:02d}".format(dt.day)
if token == "D":
return str(dt.day)
if token == "Do":
return self.locale.ordinal_number(dt.day)
if token == "dddd":
return self.locale.day_name(dt.isoweekday())
if token == "ddd":
return self.locale.day_abbreviation(dt.isoweekday())
if token == "d":
return str(dt.isoweekday())
if token == "HH":
return "{:02d}".format(dt.hour)
if token == "H":
return str(dt.hour)
if token == "hh":
return "{:02d}".format(dt.hour if 0 < dt.hour < 13 else abs(dt.hour - 12))
if token == "h":
return str(dt.hour if 0 < dt.hour < 13 else abs(dt.hour - 12))
if token == "mm":
return "{:02d}".format(dt.minute)
if token == "m":
return str(dt.minute)
if token == "ss":
return "{:02d}".format(dt.second)
if token == "s":
return str(dt.second)
if token == "SSSSSS":
return str("{:06d}".format(int(dt.microsecond)))
if token == "SSSSS":
return str("{:05d}".format(int(dt.microsecond / 10)))
if token == "SSSS":
return str("{:04d}".format(int(dt.microsecond / 100)))
if token == "SSS":
return str("{:03d}".format(int(dt.microsecond / 1000)))
if token == "SS":
return str("{:02d}".format(int(dt.microsecond / 10000)))
if token == "S":
return str(int(dt.microsecond / 100000))
if token == "X":
# TODO: replace with a call to dt.timestamp() when we drop Python 2.7
return str(calendar.timegm(dt.utctimetuple()))
if token == "x":
# TODO: replace with a call to dt.timestamp() when we drop Python 2.7
ts = calendar.timegm(dt.utctimetuple()) + (dt.microsecond / 1000000)
return str(int(ts * 1000000))
if token == "ZZZ":
return dt.tzname()
if token in ["ZZ", "Z"]:
separator = ":" if token == "ZZ" else ""
tz = dateutil_tz.tzutc() if dt.tzinfo is None else dt.tzinfo
total_minutes = int(util.total_seconds(tz.utcoffset(dt)) / 60)
sign = "+" if total_minutes >= 0 else "-"
total_minutes = abs(total_minutes)
hour, minute = divmod(total_minutes, 60)
return "{}{:02d}{}{:02d}".format(sign, hour, separator, minute)
if token in ("a", "A"):
return self.locale.meridian(dt.hour, token)
if token == "W":
year, week, day = dt.isocalendar()
return "{}-W{:02d}-{}".format(year, week, day)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,596 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
import re
from datetime import datetime, timedelta
from dateutil import tz
from arrow import locales
from arrow.util import iso_to_gregorian, next_weekday, normalize_timestamp
try:
from functools import lru_cache
except ImportError: # pragma: no cover
from backports.functools_lru_cache import lru_cache # pragma: no cover
class ParserError(ValueError):
pass
# Allows for ParserErrors to be propagated from _build_datetime()
# when day_of_year errors occur.
# Before this, the ParserErrors were caught by the try/except in
# _parse_multiformat() and the appropriate error message was not
# transmitted to the user.
class ParserMatchError(ParserError):
pass
class DateTimeParser(object):
_FORMAT_RE = re.compile(
r"(YYY?Y?|MM?M?M?|Do|DD?D?D?|d?d?d?d|HH?|hh?|mm?|ss?|S+|ZZ?Z?|a|A|x|X|W)"
)
_ESCAPE_RE = re.compile(r"\[[^\[\]]*\]")
_ONE_OR_TWO_DIGIT_RE = re.compile(r"\d{1,2}")
_ONE_OR_TWO_OR_THREE_DIGIT_RE = re.compile(r"\d{1,3}")
_ONE_OR_MORE_DIGIT_RE = re.compile(r"\d+")
_TWO_DIGIT_RE = re.compile(r"\d{2}")
_THREE_DIGIT_RE = re.compile(r"\d{3}")
_FOUR_DIGIT_RE = re.compile(r"\d{4}")
_TZ_Z_RE = re.compile(r"([\+\-])(\d{2})(?:(\d{2}))?|Z")
_TZ_ZZ_RE = re.compile(r"([\+\-])(\d{2})(?:\:(\d{2}))?|Z")
_TZ_NAME_RE = re.compile(r"\w[\w+\-/]+")
# NOTE: timestamps cannot be parsed from natural language strings (by removing the ^...$) because it will
# break cases like "15 Jul 2000" and a format list (see issue #447)
_TIMESTAMP_RE = re.compile(r"^\-?\d+\.?\d+$")
_TIMESTAMP_EXPANDED_RE = re.compile(r"^\-?\d+$")
_TIME_RE = re.compile(r"^(\d{2})(?:\:?(\d{2}))?(?:\:?(\d{2}))?(?:([\.\,])(\d+))?$")
_WEEK_DATE_RE = re.compile(r"(?P<year>\d{4})[\-]?W(?P<week>\d{2})[\-]?(?P<day>\d)?")
_BASE_INPUT_RE_MAP = {
"YYYY": _FOUR_DIGIT_RE,
"YY": _TWO_DIGIT_RE,
"MM": _TWO_DIGIT_RE,
"M": _ONE_OR_TWO_DIGIT_RE,
"DDDD": _THREE_DIGIT_RE,
"DDD": _ONE_OR_TWO_OR_THREE_DIGIT_RE,
"DD": _TWO_DIGIT_RE,
"D": _ONE_OR_TWO_DIGIT_RE,
"HH": _TWO_DIGIT_RE,
"H": _ONE_OR_TWO_DIGIT_RE,
"hh": _TWO_DIGIT_RE,
"h": _ONE_OR_TWO_DIGIT_RE,
"mm": _TWO_DIGIT_RE,
"m": _ONE_OR_TWO_DIGIT_RE,
"ss": _TWO_DIGIT_RE,
"s": _ONE_OR_TWO_DIGIT_RE,
"X": _TIMESTAMP_RE,
"x": _TIMESTAMP_EXPANDED_RE,
"ZZZ": _TZ_NAME_RE,
"ZZ": _TZ_ZZ_RE,
"Z": _TZ_Z_RE,
"S": _ONE_OR_MORE_DIGIT_RE,
"W": _WEEK_DATE_RE,
}
SEPARATORS = ["-", "/", "."]
def __init__(self, locale="en_us", cache_size=0):
self.locale = locales.get_locale(locale)
self._input_re_map = self._BASE_INPUT_RE_MAP.copy()
self._input_re_map.update(
{
"MMMM": self._generate_choice_re(
self.locale.month_names[1:], re.IGNORECASE
),
"MMM": self._generate_choice_re(
self.locale.month_abbreviations[1:], re.IGNORECASE
),
"Do": re.compile(self.locale.ordinal_day_re),
"dddd": self._generate_choice_re(
self.locale.day_names[1:], re.IGNORECASE
),
"ddd": self._generate_choice_re(
self.locale.day_abbreviations[1:], re.IGNORECASE
),
"d": re.compile(r"[1-7]"),
"a": self._generate_choice_re(
(self.locale.meridians["am"], self.locale.meridians["pm"])
),
# note: 'A' token accepts both 'am/pm' and 'AM/PM' formats to
# ensure backwards compatibility of this token
"A": self._generate_choice_re(self.locale.meridians.values()),
}
)
if cache_size > 0:
self._generate_pattern_re = lru_cache(maxsize=cache_size)(
self._generate_pattern_re
)
# TODO: since we support more than ISO 8601, we should rename this function
# IDEA: break into multiple functions
def parse_iso(self, datetime_string, normalize_whitespace=False):
if normalize_whitespace:
datetime_string = re.sub(r"\s+", " ", datetime_string.strip())
has_space_divider = " " in datetime_string
has_t_divider = "T" in datetime_string
num_spaces = datetime_string.count(" ")
if has_space_divider and num_spaces != 1 or has_t_divider and num_spaces > 0:
raise ParserError(
"Expected an ISO 8601-like string, but was given '{}'. Try passing in a format string to resolve this.".format(
datetime_string
)
)
has_time = has_space_divider or has_t_divider
has_tz = False
# date formats (ISO 8601 and others) to test against
# NOTE: YYYYMM is omitted to avoid confusion with YYMMDD (no longer part of ISO 8601, but is still often used)
formats = [
"YYYY-MM-DD",
"YYYY-M-DD",
"YYYY-M-D",
"YYYY/MM/DD",
"YYYY/M/DD",
"YYYY/M/D",
"YYYY.MM.DD",
"YYYY.M.DD",
"YYYY.M.D",
"YYYYMMDD",
"YYYY-DDDD",
"YYYYDDDD",
"YYYY-MM",
"YYYY/MM",
"YYYY.MM",
"YYYY",
"W",
]
if has_time:
if has_space_divider:
date_string, time_string = datetime_string.split(" ", 1)
else:
date_string, time_string = datetime_string.split("T", 1)
time_parts = re.split(r"[\+\-Z]", time_string, 1, re.IGNORECASE)
time_components = self._TIME_RE.match(time_parts[0])
if time_components is None:
raise ParserError(
"Invalid time component provided. Please specify a format or provide a valid time component in the basic or extended ISO 8601 time format."
)
(
hours,
minutes,
seconds,
subseconds_sep,
subseconds,
) = time_components.groups()
has_tz = len(time_parts) == 2
has_minutes = minutes is not None
has_seconds = seconds is not None
has_subseconds = subseconds is not None
is_basic_time_format = ":" not in time_parts[0]
tz_format = "Z"
# use 'ZZ' token instead since tz offset is present in non-basic format
if has_tz and ":" in time_parts[1]:
tz_format = "ZZ"
time_sep = "" if is_basic_time_format else ":"
if has_subseconds:
time_string = "HH{time_sep}mm{time_sep}ss{subseconds_sep}S".format(
time_sep=time_sep, subseconds_sep=subseconds_sep
)
elif has_seconds:
time_string = "HH{time_sep}mm{time_sep}ss".format(time_sep=time_sep)
elif has_minutes:
time_string = "HH{time_sep}mm".format(time_sep=time_sep)
else:
time_string = "HH"
if has_space_divider:
formats = ["{} {}".format(f, time_string) for f in formats]
else:
formats = ["{}T{}".format(f, time_string) for f in formats]
if has_time and has_tz:
# Add "Z" or "ZZ" to the format strings to indicate to
# _parse_token() that a timezone needs to be parsed
formats = ["{}{}".format(f, tz_format) for f in formats]
return self._parse_multiformat(datetime_string, formats)
def parse(self, datetime_string, fmt, normalize_whitespace=False):
if normalize_whitespace:
datetime_string = re.sub(r"\s+", " ", datetime_string)
if isinstance(fmt, list):
return self._parse_multiformat(datetime_string, fmt)
fmt_tokens, fmt_pattern_re = self._generate_pattern_re(fmt)
match = fmt_pattern_re.search(datetime_string)
if match is None:
raise ParserMatchError(
"Failed to match '{}' when parsing '{}'".format(fmt, datetime_string)
)
parts = {}
for token in fmt_tokens:
if token == "Do":
value = match.group("value")
elif token == "W":
value = (match.group("year"), match.group("week"), match.group("day"))
else:
value = match.group(token)
self._parse_token(token, value, parts)
return self._build_datetime(parts)
def _generate_pattern_re(self, fmt):
# fmt is a string of tokens like 'YYYY-MM-DD'
# we construct a new string by replacing each
# token by its pattern:
# 'YYYY-MM-DD' -> '(?P<YYYY>\d{4})-(?P<MM>\d{2})-(?P<DD>\d{2})'
tokens = []
offset = 0
# Escape all special RegEx chars
escaped_fmt = re.escape(fmt)
# Extract the bracketed expressions to be reinserted later.
escaped_fmt = re.sub(self._ESCAPE_RE, "#", escaped_fmt)
# Any number of S is the same as one.
# TODO: allow users to specify the number of digits to parse
escaped_fmt = re.sub(r"S+", "S", escaped_fmt)
escaped_data = re.findall(self._ESCAPE_RE, fmt)
fmt_pattern = escaped_fmt
for m in self._FORMAT_RE.finditer(escaped_fmt):
token = m.group(0)
try:
input_re = self._input_re_map[token]
except KeyError:
raise ParserError("Unrecognized token '{}'".format(token))
input_pattern = "(?P<{}>{})".format(token, input_re.pattern)
tokens.append(token)
# a pattern doesn't have the same length as the token
# it replaces! We keep the difference in the offset variable.
# This works because the string is scanned left-to-right and matches
# are returned in the order found by finditer.
fmt_pattern = (
fmt_pattern[: m.start() + offset]
+ input_pattern
+ fmt_pattern[m.end() + offset :]
)
offset += len(input_pattern) - (m.end() - m.start())
final_fmt_pattern = ""
split_fmt = fmt_pattern.split(r"\#")
# Due to the way Python splits, 'split_fmt' will always be longer
for i in range(len(split_fmt)):
final_fmt_pattern += split_fmt[i]
if i < len(escaped_data):
final_fmt_pattern += escaped_data[i][1:-1]
# Wrap final_fmt_pattern in a custom word boundary to strictly
# match the formatting pattern and filter out date and time formats
# that include junk such as: blah1998-09-12 blah, blah 1998-09-12blah,
# blah1998-09-12blah. The custom word boundary matches every character
# that is not a whitespace character to allow for searching for a date
# and time string in a natural language sentence. Therefore, searching
# for a string of the form YYYY-MM-DD in "blah 1998-09-12 blah" will
# work properly.
# Certain punctuation before or after the target pattern such as
# "1998-09-12," is permitted. For the full list of valid punctuation,
# see the documentation.
starting_word_boundary = (
r"(?<!\S\S)" # Don't have two consecutive non-whitespace characters. This ensures that we allow cases like .11.25.2019 but not 1.11.25.2019 (for pattern MM.DD.YYYY)
r"(?<![^\,\.\;\:\?\!\"\'\`\[\]\{\}\(\)<>\s])" # This is the list of punctuation that is ok before the pattern (i.e. "It can't not be these characters before the pattern")
r"(\b|^)" # The \b is to block cases like 1201912 but allow 201912 for pattern YYYYMM. The ^ was necessary to allow a negative number through i.e. before epoch numbers
)
ending_word_boundary = (
r"(?=[\,\.\;\:\?\!\"\'\`\[\]\{\}\(\)\<\>]?" # Positive lookahead stating that these punctuation marks can appear after the pattern at most 1 time
r"(?!\S))" # Don't allow any non-whitespace character after the punctuation
)
bounded_fmt_pattern = r"{}{}{}".format(
starting_word_boundary, final_fmt_pattern, ending_word_boundary
)
return tokens, re.compile(bounded_fmt_pattern, flags=re.IGNORECASE)
def _parse_token(self, token, value, parts):
if token == "YYYY":
parts["year"] = int(value)
elif token == "YY":
value = int(value)
parts["year"] = 1900 + value if value > 68 else 2000 + value
elif token in ["MMMM", "MMM"]:
parts["month"] = self.locale.month_number(value.lower())
elif token in ["MM", "M"]:
parts["month"] = int(value)
elif token in ["DDDD", "DDD"]:
parts["day_of_year"] = int(value)
elif token in ["DD", "D"]:
parts["day"] = int(value)
elif token == "Do":
parts["day"] = int(value)
elif token == "dddd":
# locale day names are 1-indexed
day_of_week = [x.lower() for x in self.locale.day_names].index(
value.lower()
)
parts["day_of_week"] = day_of_week - 1
elif token == "ddd":
# locale day abbreviations are 1-indexed
day_of_week = [x.lower() for x in self.locale.day_abbreviations].index(
value.lower()
)
parts["day_of_week"] = day_of_week - 1
elif token.upper() in ["HH", "H"]:
parts["hour"] = int(value)
elif token in ["mm", "m"]:
parts["minute"] = int(value)
elif token in ["ss", "s"]:
parts["second"] = int(value)
elif token == "S":
# We have the *most significant* digits of an arbitrary-precision integer.
# We want the six most significant digits as an integer, rounded.
# IDEA: add nanosecond support somehow? Need datetime support for it first.
value = value.ljust(7, str("0"))
# floating-point (IEEE-754) defaults to half-to-even rounding
seventh_digit = int(value[6])
if seventh_digit == 5:
rounding = int(value[5]) % 2
elif seventh_digit > 5:
rounding = 1
else:
rounding = 0
parts["microsecond"] = int(value[:6]) + rounding
elif token == "X":
parts["timestamp"] = float(value)
elif token == "x":
parts["expanded_timestamp"] = int(value)
elif token in ["ZZZ", "ZZ", "Z"]:
parts["tzinfo"] = TzinfoParser.parse(value)
elif token in ["a", "A"]:
if value in (self.locale.meridians["am"], self.locale.meridians["AM"]):
parts["am_pm"] = "am"
elif value in (self.locale.meridians["pm"], self.locale.meridians["PM"]):
parts["am_pm"] = "pm"
elif token == "W":
parts["weekdate"] = value
@staticmethod
def _build_datetime(parts):
weekdate = parts.get("weekdate")
if weekdate is not None:
# we can use strptime (%G, %V, %u) in python 3.6 but these tokens aren't available before that
year, week = int(weekdate[0]), int(weekdate[1])
if weekdate[2] is not None:
day = int(weekdate[2])
else:
# day not given, default to 1
day = 1
dt = iso_to_gregorian(year, week, day)
parts["year"] = dt.year
parts["month"] = dt.month
parts["day"] = dt.day
timestamp = parts.get("timestamp")
if timestamp is not None:
return datetime.fromtimestamp(timestamp, tz=tz.tzutc())
expanded_timestamp = parts.get("expanded_timestamp")
if expanded_timestamp is not None:
return datetime.fromtimestamp(
normalize_timestamp(expanded_timestamp),
tz=tz.tzutc(),
)
day_of_year = parts.get("day_of_year")
if day_of_year is not None:
year = parts.get("year")
month = parts.get("month")
if year is None:
raise ParserError(
"Year component is required with the DDD and DDDD tokens."
)
if month is not None:
raise ParserError(
"Month component is not allowed with the DDD and DDDD tokens."
)
date_string = "{}-{}".format(year, day_of_year)
try:
dt = datetime.strptime(date_string, "%Y-%j")
except ValueError:
raise ParserError(
"The provided day of year '{}' is invalid.".format(day_of_year)
)
parts["year"] = dt.year
parts["month"] = dt.month
parts["day"] = dt.day
day_of_week = parts.get("day_of_week")
day = parts.get("day")
# If day is passed, ignore day of week
if day_of_week is not None and day is None:
year = parts.get("year", 1970)
month = parts.get("month", 1)
day = 1
# dddd => first day of week after epoch
# dddd YYYY => first day of week in specified year
# dddd MM YYYY => first day of week in specified year and month
# dddd MM => first day after epoch in specified month
next_weekday_dt = next_weekday(datetime(year, month, day), day_of_week)
parts["year"] = next_weekday_dt.year
parts["month"] = next_weekday_dt.month
parts["day"] = next_weekday_dt.day
am_pm = parts.get("am_pm")
hour = parts.get("hour", 0)
if am_pm == "pm" and hour < 12:
hour += 12
elif am_pm == "am" and hour == 12:
hour = 0
# Support for midnight at the end of day
if hour == 24:
if parts.get("minute", 0) != 0:
raise ParserError("Midnight at the end of day must not contain minutes")
if parts.get("second", 0) != 0:
raise ParserError("Midnight at the end of day must not contain seconds")
if parts.get("microsecond", 0) != 0:
raise ParserError(
"Midnight at the end of day must not contain microseconds"
)
hour = 0
day_increment = 1
else:
day_increment = 0
# account for rounding up to 1000000
microsecond = parts.get("microsecond", 0)
if microsecond == 1000000:
microsecond = 0
second_increment = 1
else:
second_increment = 0
increment = timedelta(days=day_increment, seconds=second_increment)
return (
datetime(
year=parts.get("year", 1),
month=parts.get("month", 1),
day=parts.get("day", 1),
hour=hour,
minute=parts.get("minute", 0),
second=parts.get("second", 0),
microsecond=microsecond,
tzinfo=parts.get("tzinfo"),
)
+ increment
)
def _parse_multiformat(self, string, formats):
_datetime = None
for fmt in formats:
try:
_datetime = self.parse(string, fmt)
break
except ParserMatchError:
pass
if _datetime is None:
raise ParserError(
"Could not match input '{}' to any of the following formats: {}".format(
string, ", ".join(formats)
)
)
return _datetime
# generates a capture group of choices separated by an OR operator
@staticmethod
def _generate_choice_re(choices, flags=0):
return re.compile(r"({})".format("|".join(choices)), flags=flags)
class TzinfoParser(object):
_TZINFO_RE = re.compile(r"^([\+\-])?(\d{2})(?:\:?(\d{2}))?$")
@classmethod
def parse(cls, tzinfo_string):
tzinfo = None
if tzinfo_string == "local":
tzinfo = tz.tzlocal()
elif tzinfo_string in ["utc", "UTC", "Z"]:
tzinfo = tz.tzutc()
else:
iso_match = cls._TZINFO_RE.match(tzinfo_string)
if iso_match:
sign, hours, minutes = iso_match.groups()
if minutes is None:
minutes = 0
seconds = int(hours) * 3600 + int(minutes) * 60
if sign == "-":
seconds *= -1
tzinfo = tz.tzoffset(None, seconds)
else:
tzinfo = tz.gettz(tzinfo_string)
if tzinfo is None:
raise ParserError(
'Could not parse timezone expression "{}"'.format(tzinfo_string)
)
return tzinfo

View file

@ -0,0 +1,115 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import
import datetime
import numbers
from dateutil.rrule import WEEKLY, rrule
from arrow.constants import MAX_TIMESTAMP, MAX_TIMESTAMP_MS, MAX_TIMESTAMP_US
def next_weekday(start_date, weekday):
"""Get next weekday from the specified start date.
:param start_date: Datetime object representing the start date.
:param weekday: Next weekday to obtain. Can be a value between 0 (Monday) and 6 (Sunday).
:return: Datetime object corresponding to the next weekday after start_date.
Usage::
# Get first Monday after epoch
>>> next_weekday(datetime(1970, 1, 1), 0)
1970-01-05 00:00:00
# Get first Thursday after epoch
>>> next_weekday(datetime(1970, 1, 1), 3)
1970-01-01 00:00:00
# Get first Sunday after epoch
>>> next_weekday(datetime(1970, 1, 1), 6)
1970-01-04 00:00:00
"""
if weekday < 0 or weekday > 6:
raise ValueError("Weekday must be between 0 (Monday) and 6 (Sunday).")
return rrule(freq=WEEKLY, dtstart=start_date, byweekday=weekday, count=1)[0]
def total_seconds(td):
"""Get total seconds for timedelta."""
return td.total_seconds()
def is_timestamp(value):
"""Check if value is a valid timestamp."""
if isinstance(value, bool):
return False
if not (
isinstance(value, numbers.Integral)
or isinstance(value, float)
or isinstance(value, str)
):
return False
try:
float(value)
return True
except ValueError:
return False
def normalize_timestamp(timestamp):
"""Normalize millisecond and microsecond timestamps into normal timestamps."""
if timestamp > MAX_TIMESTAMP:
if timestamp < MAX_TIMESTAMP_MS:
timestamp /= 1e3
elif timestamp < MAX_TIMESTAMP_US:
timestamp /= 1e6
else:
raise ValueError(
"The specified timestamp '{}' is too large.".format(timestamp)
)
return timestamp
# Credit to https://stackoverflow.com/a/1700069
def iso_to_gregorian(iso_year, iso_week, iso_day):
"""Converts an ISO week date tuple into a datetime object."""
if not 1 <= iso_week <= 53:
raise ValueError("ISO Calendar week value must be between 1-53.")
if not 1 <= iso_day <= 7:
raise ValueError("ISO Calendar day value must be between 1-7")
# The first week of the year always contains 4 Jan.
fourth_jan = datetime.date(iso_year, 1, 4)
delta = datetime.timedelta(fourth_jan.isoweekday() - 1)
year_start = fourth_jan - delta
gregorian = year_start + datetime.timedelta(days=iso_day - 1, weeks=iso_week - 1)
return gregorian
def validate_bounds(bounds):
if bounds != "()" and bounds != "(]" and bounds != "[)" and bounds != "[]":
raise ValueError(
'Invalid bounds. Please select between "()", "(]", "[)", or "[]".'
)
# Python 2.7 / 3.0+ definitions for isstr function.
try: # pragma: no cover
basestring
def isstr(s):
return isinstance(s, basestring) # noqa: F821
except NameError: # pragma: no cover
def isstr(s):
return isinstance(s, str)
__all__ = ["next_weekday", "total_seconds", "is_timestamp", "isstr", "iso_to_gregorian"]

View file

@ -0,0 +1,20 @@
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS ?=
SPHINXBUILD ?= sphinx-build
SOURCEDIR = .
BUILDDIR = _build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

View file

@ -0,0 +1,62 @@
# -*- coding: utf-8 -*-
# -- Path setup --------------------------------------------------------------
import io
import os
import sys
sys.path.insert(0, os.path.abspath(".."))
about = {}
with io.open("../arrow/_version.py", "r", encoding="utf-8") as f:
exec(f.read(), about)
# -- Project information -----------------------------------------------------
project = u"Arrow 🏹"
copyright = "2020, Chris Smith"
author = "Chris Smith"
release = about["__version__"]
# -- General configuration ---------------------------------------------------
extensions = ["sphinx.ext.autodoc"]
templates_path = []
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
master_doc = "index"
source_suffix = ".rst"
pygments_style = "sphinx"
language = None
# -- Options for HTML output -------------------------------------------------
html_theme = "alabaster"
html_theme_path = []
html_static_path = []
html_show_sourcelink = False
html_show_sphinx = False
html_show_copyright = True
# https://alabaster.readthedocs.io/en/latest/customization.html
html_theme_options = {
"description": "Arrow is a sensible and human-friendly approach to dates, times and timestamps.",
"github_user": "arrow-py",
"github_repo": "arrow",
"github_banner": True,
"show_related": False,
"show_powered_by": False,
"github_button": True,
"github_type": "star",
"github_count": "true", # must be a string
}
html_sidebars = {
"**": ["about.html", "localtoc.html", "relations.html", "searchbox.html"]
}

View file

@ -0,0 +1,566 @@
Arrow: Better dates & times for Python
======================================
Release v\ |release| (`Installation`_) (`Changelog <releases.html>`_)
.. include:: ../README.rst
:start-after: start-inclusion-marker-do-not-remove
:end-before: end-inclusion-marker-do-not-remove
User's Guide
------------
Creation
~~~~~~~~
Get 'now' easily:
.. code-block:: python
>>> arrow.utcnow()
<Arrow [2013-05-07T04:20:39.369271+00:00]>
>>> arrow.now()
<Arrow [2013-05-06T21:20:40.841085-07:00]>
>>> arrow.now('US/Pacific')
<Arrow [2013-05-06T21:20:44.761511-07:00]>
Create from timestamps (:code:`int` or :code:`float`):
.. code-block:: python
>>> arrow.get(1367900664)
<Arrow [2013-05-07T04:24:24+00:00]>
>>> arrow.get(1367900664.152325)
<Arrow [2013-05-07T04:24:24.152325+00:00]>
Use a naive or timezone-aware datetime, or flexibly specify a timezone:
.. code-block:: python
>>> arrow.get(datetime.utcnow())
<Arrow [2013-05-07T04:24:24.152325+00:00]>
>>> arrow.get(datetime(2013, 5, 5), 'US/Pacific')
<Arrow [2013-05-05T00:00:00-07:00]>
>>> from dateutil import tz
>>> arrow.get(datetime(2013, 5, 5), tz.gettz('US/Pacific'))
<Arrow [2013-05-05T00:00:00-07:00]>
>>> arrow.get(datetime.now(tz.gettz('US/Pacific')))
<Arrow [2013-05-06T21:24:49.552236-07:00]>
Parse from a string:
.. code-block:: python
>>> arrow.get('2013-05-05 12:30:45', 'YYYY-MM-DD HH:mm:ss')
<Arrow [2013-05-05T12:30:45+00:00]>
Search a date in a string:
.. code-block:: python
>>> arrow.get('June was born in May 1980', 'MMMM YYYY')
<Arrow [1980-05-01T00:00:00+00:00]>
Some ISO 8601 compliant strings are recognized and parsed without a format string:
>>> arrow.get('2013-09-30T15:34:00.000-07:00')
<Arrow [2013-09-30T15:34:00-07:00]>
Arrow objects can be instantiated directly too, with the same arguments as a datetime:
.. code-block:: python
>>> arrow.get(2013, 5, 5)
<Arrow [2013-05-05T00:00:00+00:00]>
>>> arrow.Arrow(2013, 5, 5)
<Arrow [2013-05-05T00:00:00+00:00]>
Properties
~~~~~~~~~~
Get a datetime or timestamp representation:
.. code-block:: python
>>> a = arrow.utcnow()
>>> a.datetime
datetime.datetime(2013, 5, 7, 4, 38, 15, 447644, tzinfo=tzutc())
>>> a.timestamp
1367901495
Get a naive datetime, and tzinfo:
.. code-block:: python
>>> a.naive
datetime.datetime(2013, 5, 7, 4, 38, 15, 447644)
>>> a.tzinfo
tzutc()
Get any datetime value:
.. code-block:: python
>>> a.year
2013
Call datetime functions that return properties:
.. code-block:: python
>>> a.date()
datetime.date(2013, 5, 7)
>>> a.time()
datetime.time(4, 38, 15, 447644)
Replace & Shift
~~~~~~~~~~~~~~~
Get a new :class:`Arrow <arrow.arrow.Arrow>` object, with altered attributes, just as you would with a datetime:
.. code-block:: python
>>> arw = arrow.utcnow()
>>> arw
<Arrow [2013-05-12T03:29:35.334214+00:00]>
>>> arw.replace(hour=4, minute=40)
<Arrow [2013-05-12T04:40:35.334214+00:00]>
Or, get one with attributes shifted forward or backward:
.. code-block:: python
>>> arw.shift(weeks=+3)
<Arrow [2013-06-02T03:29:35.334214+00:00]>
Even replace the timezone without altering other attributes:
.. code-block:: python
>>> arw.replace(tzinfo='US/Pacific')
<Arrow [2013-05-12T03:29:35.334214-07:00]>
Move between the earlier and later moments of an ambiguous time:
.. code-block:: python
>>> paris_transition = arrow.Arrow(2019, 10, 27, 2, tzinfo="Europe/Paris", fold=0)
>>> paris_transition
<Arrow [2019-10-27T02:00:00+02:00]>
>>> paris_transition.ambiguous
True
>>> paris_transition.replace(fold=1)
<Arrow [2019-10-27T02:00:00+01:00]>
Format
~~~~~~
.. code-block:: python
>>> arrow.utcnow().format('YYYY-MM-DD HH:mm:ss ZZ')
'2013-05-07 05:23:16 -00:00'
Convert
~~~~~~~
Convert from UTC to other timezones by name or tzinfo:
.. code-block:: python
>>> utc = arrow.utcnow()
>>> utc
<Arrow [2013-05-07T05:24:11.823627+00:00]>
>>> utc.to('US/Pacific')
<Arrow [2013-05-06T22:24:11.823627-07:00]>
>>> utc.to(tz.gettz('US/Pacific'))
<Arrow [2013-05-06T22:24:11.823627-07:00]>
Or using shorthand:
.. code-block:: python
>>> utc.to('local')
<Arrow [2013-05-06T22:24:11.823627-07:00]>
>>> utc.to('local').to('utc')
<Arrow [2013-05-07T05:24:11.823627+00:00]>
Humanize
~~~~~~~~
Humanize relative to now:
.. code-block:: python
>>> past = arrow.utcnow().shift(hours=-1)
>>> past.humanize()
'an hour ago'
Or another Arrow, or datetime:
.. code-block:: python
>>> present = arrow.utcnow()
>>> future = present.shift(hours=2)
>>> future.humanize(present)
'in 2 hours'
Indicate time as relative or include only the distance
.. code-block:: python
>>> present = arrow.utcnow()
>>> future = present.shift(hours=2)
>>> future.humanize(present)
'in 2 hours'
>>> future.humanize(present, only_distance=True)
'2 hours'
Indicate a specific time granularity (or multiple):
.. code-block:: python
>>> present = arrow.utcnow()
>>> future = present.shift(minutes=66)
>>> future.humanize(present, granularity="minute")
'in 66 minutes'
>>> future.humanize(present, granularity=["hour", "minute"])
'in an hour and 6 minutes'
>>> present.humanize(future, granularity=["hour", "minute"])
'an hour and 6 minutes ago'
>>> future.humanize(present, only_distance=True, granularity=["hour", "minute"])
'an hour and 6 minutes'
Support for a growing number of locales (see ``locales.py`` for supported languages):
.. code-block:: python
>>> future = arrow.utcnow().shift(hours=1)
>>> future.humanize(a, locale='ru')
'через 2 час(а,ов)'
Ranges & Spans
~~~~~~~~~~~~~~
Get the time span of any unit:
.. code-block:: python
>>> arrow.utcnow().span('hour')
(<Arrow [2013-05-07T05:00:00+00:00]>, <Arrow [2013-05-07T05:59:59.999999+00:00]>)
Or just get the floor and ceiling:
.. code-block:: python
>>> arrow.utcnow().floor('hour')
<Arrow [2013-05-07T05:00:00+00:00]>
>>> arrow.utcnow().ceil('hour')
<Arrow [2013-05-07T05:59:59.999999+00:00]>
You can also get a range of time spans:
.. code-block:: python
>>> start = datetime(2013, 5, 5, 12, 30)
>>> end = datetime(2013, 5, 5, 17, 15)
>>> for r in arrow.Arrow.span_range('hour', start, end):
... print r
...
(<Arrow [2013-05-05T12:00:00+00:00]>, <Arrow [2013-05-05T12:59:59.999999+00:00]>)
(<Arrow [2013-05-05T13:00:00+00:00]>, <Arrow [2013-05-05T13:59:59.999999+00:00]>)
(<Arrow [2013-05-05T14:00:00+00:00]>, <Arrow [2013-05-05T14:59:59.999999+00:00]>)
(<Arrow [2013-05-05T15:00:00+00:00]>, <Arrow [2013-05-05T15:59:59.999999+00:00]>)
(<Arrow [2013-05-05T16:00:00+00:00]>, <Arrow [2013-05-05T16:59:59.999999+00:00]>)
Or just iterate over a range of time:
.. code-block:: python
>>> start = datetime(2013, 5, 5, 12, 30)
>>> end = datetime(2013, 5, 5, 17, 15)
>>> for r in arrow.Arrow.range('hour', start, end):
... print repr(r)
...
<Arrow [2013-05-05T12:30:00+00:00]>
<Arrow [2013-05-05T13:30:00+00:00]>
<Arrow [2013-05-05T14:30:00+00:00]>
<Arrow [2013-05-05T15:30:00+00:00]>
<Arrow [2013-05-05T16:30:00+00:00]>
.. toctree::
:maxdepth: 2
Factories
~~~~~~~~~
Use factories to harness Arrow's module API for a custom Arrow-derived type. First, derive your type:
.. code-block:: python
>>> class CustomArrow(arrow.Arrow):
...
... def days_till_xmas(self):
...
... xmas = arrow.Arrow(self.year, 12, 25)
...
... if self > xmas:
... xmas = xmas.shift(years=1)
...
... return (xmas - self).days
Then get and use a factory for it:
.. code-block:: python
>>> factory = arrow.ArrowFactory(CustomArrow)
>>> custom = factory.utcnow()
>>> custom
>>> <CustomArrow [2013-05-27T23:35:35.533160+00:00]>
>>> custom.days_till_xmas()
>>> 211
Supported Tokens
~~~~~~~~~~~~~~~~
Use the following tokens for parsing and formatting. Note that they are **not** the same as the tokens for `strptime <https://linux.die.net/man/3/strptime>`_:
+--------------------------------+--------------+-------------------------------------------+
| |Token |Output |
+================================+==============+===========================================+
|**Year** |YYYY |2000, 2001, 2002 ... 2012, 2013 |
+--------------------------------+--------------+-------------------------------------------+
| |YY |00, 01, 02 ... 12, 13 |
+--------------------------------+--------------+-------------------------------------------+
|**Month** |MMMM |January, February, March ... [#t1]_ |
+--------------------------------+--------------+-------------------------------------------+
| |MMM |Jan, Feb, Mar ... [#t1]_ |
+--------------------------------+--------------+-------------------------------------------+
| |MM |01, 02, 03 ... 11, 12 |
+--------------------------------+--------------+-------------------------------------------+
| |M |1, 2, 3 ... 11, 12 |
+--------------------------------+--------------+-------------------------------------------+
|**Day of Year** |DDDD |001, 002, 003 ... 364, 365 |
+--------------------------------+--------------+-------------------------------------------+
| |DDD |1, 2, 3 ... 364, 365 |
+--------------------------------+--------------+-------------------------------------------+
|**Day of Month** |DD |01, 02, 03 ... 30, 31 |
+--------------------------------+--------------+-------------------------------------------+
| |D |1, 2, 3 ... 30, 31 |
+--------------------------------+--------------+-------------------------------------------+
| |Do |1st, 2nd, 3rd ... 30th, 31st |
+--------------------------------+--------------+-------------------------------------------+
|**Day of Week** |dddd |Monday, Tuesday, Wednesday ... [#t2]_ |
+--------------------------------+--------------+-------------------------------------------+
| |ddd |Mon, Tue, Wed ... [#t2]_ |
+--------------------------------+--------------+-------------------------------------------+
| |d |1, 2, 3 ... 6, 7 |
+--------------------------------+--------------+-------------------------------------------+
|**ISO week date** |W |2011-W05-4, 2019-W17 |
+--------------------------------+--------------+-------------------------------------------+
|**Hour** |HH |00, 01, 02 ... 23, 24 |
+--------------------------------+--------------+-------------------------------------------+
| |H |0, 1, 2 ... 23, 24 |
+--------------------------------+--------------+-------------------------------------------+
| |hh |01, 02, 03 ... 11, 12 |
+--------------------------------+--------------+-------------------------------------------+
| |h |1, 2, 3 ... 11, 12 |
+--------------------------------+--------------+-------------------------------------------+
|**AM / PM** |A |AM, PM, am, pm [#t1]_ |
+--------------------------------+--------------+-------------------------------------------+
| |a |am, pm [#t1]_ |
+--------------------------------+--------------+-------------------------------------------+
|**Minute** |mm |00, 01, 02 ... 58, 59 |
+--------------------------------+--------------+-------------------------------------------+
| |m |0, 1, 2 ... 58, 59 |
+--------------------------------+--------------+-------------------------------------------+
|**Second** |ss |00, 01, 02 ... 58, 59 |
+--------------------------------+--------------+-------------------------------------------+
| |s |0, 1, 2 ... 58, 59 |
+--------------------------------+--------------+-------------------------------------------+
|**Sub-second** |S... |0, 02, 003, 000006, 123123123123... [#t3]_ |
+--------------------------------+--------------+-------------------------------------------+
|**Timezone** |ZZZ |Asia/Baku, Europe/Warsaw, GMT ... [#t4]_ |
+--------------------------------+--------------+-------------------------------------------+
| |ZZ |-07:00, -06:00 ... +06:00, +07:00, +08, Z |
+--------------------------------+--------------+-------------------------------------------+
| |Z |-0700, -0600 ... +0600, +0700, +08, Z |
+--------------------------------+--------------+-------------------------------------------+
|**Seconds Timestamp** |X |1381685817, 1381685817.915482 ... [#t5]_ |
+--------------------------------+--------------+-------------------------------------------+
|**ms or µs Timestamp** |x |1569980330813, 1569980330813221 |
+--------------------------------+--------------+-------------------------------------------+
.. rubric:: Footnotes
.. [#t1] localization support for parsing and formatting
.. [#t2] localization support only for formatting
.. [#t3] the result is truncated to microseconds, with `half-to-even rounding <https://en.wikipedia.org/wiki/IEEE_floating_point#Roundings_to_nearest>`_.
.. [#t4] timezone names from `tz database <https://www.iana.org/time-zones>`_ provided via dateutil package, note that abbreviations such as MST, PDT, BRST are unlikely to parse due to ambiguity. Use the full IANA zone name instead (Asia/Shanghai, Europe/London, America/Chicago etc).
.. [#t5] this token cannot be used for parsing timestamps out of natural language strings due to compatibility reasons
Built-in Formats
++++++++++++++++
There are several formatting standards that are provided as built-in tokens.
.. code-block:: python
>>> arw = arrow.utcnow()
>>> arw.format(arrow.FORMAT_ATOM)
'2020-05-27 10:30:35+00:00'
>>> arw.format(arrow.FORMAT_COOKIE)
'Wednesday, 27-May-2020 10:30:35 UTC'
>>> arw.format(arrow.FORMAT_RSS)
'Wed, 27 May 2020 10:30:35 +0000'
>>> arw.format(arrow.FORMAT_RFC822)
'Wed, 27 May 20 10:30:35 +0000'
>>> arw.format(arrow.FORMAT_RFC850)
'Wednesday, 27-May-20 10:30:35 UTC'
>>> arw.format(arrow.FORMAT_RFC1036)
'Wed, 27 May 20 10:30:35 +0000'
>>> arw.format(arrow.FORMAT_RFC1123)
'Wed, 27 May 2020 10:30:35 +0000'
>>> arw.format(arrow.FORMAT_RFC2822)
'Wed, 27 May 2020 10:30:35 +0000'
>>> arw.format(arrow.FORMAT_RFC3339)
'2020-05-27 10:30:35+00:00'
>>> arw.format(arrow.FORMAT_W3C)
'2020-05-27 10:30:35+00:00'
Escaping Formats
~~~~~~~~~~~~~~~~
Tokens, phrases, and regular expressions in a format string can be escaped when parsing and formatting by enclosing them within square brackets.
Tokens & Phrases
++++++++++++++++
Any `token <Supported Tokens_>`_ or phrase can be escaped as follows:
.. code-block:: python
>>> fmt = "YYYY-MM-DD h [h] m"
>>> arw = arrow.get("2018-03-09 8 h 40", fmt)
<Arrow [2018-03-09T08:40:00+00:00]>
>>> arw.format(fmt)
'2018-03-09 8 h 40'
>>> fmt = "YYYY-MM-DD h [hello] m"
>>> arw = arrow.get("2018-03-09 8 hello 40", fmt)
<Arrow [2018-03-09T08:40:00+00:00]>
>>> arw.format(fmt)
'2018-03-09 8 hello 40'
>>> fmt = "YYYY-MM-DD h [hello world] m"
>>> arw = arrow.get("2018-03-09 8 hello world 40", fmt)
<Arrow [2018-03-09T08:40:00+00:00]>
>>> arw.format(fmt)
'2018-03-09 8 hello world 40'
This can be useful for parsing dates in different locales such as French, in which it is common to format time strings as "8 h 40" rather than "8:40".
Regular Expressions
+++++++++++++++++++
You can also escape regular expressions by enclosing them within square brackets. In the following example, we are using the regular expression :code:`\s+` to match any number of whitespace characters that separate the tokens. This is useful if you do not know the number of spaces between tokens ahead of time (e.g. in log files).
.. code-block:: python
>>> fmt = r"ddd[\s+]MMM[\s+]DD[\s+]HH:mm:ss[\s+]YYYY"
>>> arrow.get("Mon Sep 08 16:41:45 2014", fmt)
<Arrow [2014-09-08T16:41:45+00:00]>
>>> arrow.get("Mon \tSep 08 16:41:45 2014", fmt)
<Arrow [2014-09-08T16:41:45+00:00]>
>>> arrow.get("Mon Sep 08 16:41:45 2014", fmt)
<Arrow [2014-09-08T16:41:45+00:00]>
Punctuation
~~~~~~~~~~~
Date and time formats may be fenced on either side by one punctuation character from the following list: ``, . ; : ? ! " \` ' [ ] { } ( ) < >``
.. code-block:: python
>>> arrow.get("Cool date: 2019-10-31T09:12:45.123456+04:30.", "YYYY-MM-DDTHH:mm:ss.SZZ")
<Arrow [2019-10-31T09:12:45.123456+04:30]>
>>> arrow.get("Tomorrow (2019-10-31) is Halloween!", "YYYY-MM-DD")
<Arrow [2019-10-31T00:00:00+00:00]>
>>> arrow.get("Halloween is on 2019.10.31.", "YYYY.MM.DD")
<Arrow [2019-10-31T00:00:00+00:00]>
>>> arrow.get("It's Halloween tomorrow (2019-10-31)!", "YYYY-MM-DD")
# Raises exception because there are multiple punctuation marks following the date
Redundant Whitespace
~~~~~~~~~~~~~~~~~~~~
Redundant whitespace characters (spaces, tabs, and newlines) can be normalized automatically by passing in the ``normalize_whitespace`` flag to ``arrow.get``:
.. code-block:: python
>>> arrow.get('\t \n 2013-05-05T12:30:45.123456 \t \n', normalize_whitespace=True)
<Arrow [2013-05-05T12:30:45.123456+00:00]>
>>> arrow.get('2013-05-05 T \n 12:30:45\t123456', 'YYYY-MM-DD T HH:mm:ss S', normalize_whitespace=True)
<Arrow [2013-05-05T12:30:45.123456+00:00]>
API Guide
---------
arrow.arrow
~~~~~~~~~~~
.. automodule:: arrow.arrow
:members:
arrow.factory
~~~~~~~~~~~~~
.. automodule:: arrow.factory
:members:
arrow.api
~~~~~~~~~
.. automodule:: arrow.api
:members:
arrow.locale
~~~~~~~~~~~~
.. automodule:: arrow.locales
:members:
:undoc-members:
Release History
---------------
.. toctree::
:maxdepth: 2
releases

View file

@ -0,0 +1,35 @@
@ECHO OFF
pushd %~dp0
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=.
set BUILDDIR=_build
if "%1" == "" goto help
%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.http://sphinx-doc.org/
exit /b 1
)
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
goto end
:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
:end
popd

View file

@ -0,0 +1,3 @@
.. _releases:
.. include:: ../CHANGELOG.rst

View file

@ -0,0 +1,14 @@
backports.functools_lru_cache==1.6.1; python_version == "2.7"
dateparser==0.7.*
pre-commit==1.21.*; python_version <= "3.5"
pre-commit==2.6.*; python_version >= "3.6"
pytest==4.6.*; python_version == "2.7"
pytest==6.0.*; python_version >= "3.5"
pytest-cov==2.10.*
pytest-mock==2.0.*; python_version == "2.7"
pytest-mock==3.2.*; python_version >= "3.5"
python-dateutil==2.8.*
pytz==2019.*
simplejson==3.17.*
sphinx==1.8.*; python_version == "2.7"
sphinx==3.2.*; python_version >= "3.5"

View file

@ -0,0 +1,2 @@
[bdist_wheel]
universal = 1

View file

@ -0,0 +1,50 @@
# -*- coding: utf-8 -*-
import io
from setuptools import setup
with io.open("README.rst", "r", encoding="utf-8") as f:
readme = f.read()
about = {}
with io.open("arrow/_version.py", "r", encoding="utf-8") as f:
exec(f.read(), about)
setup(
name="arrow",
version=about["__version__"],
description="Better dates & times for Python",
long_description=readme,
long_description_content_type="text/x-rst",
url="https://arrow.readthedocs.io",
author="Chris Smith",
author_email="crsmithdev@gmail.com",
license="Apache 2.0",
packages=["arrow"],
zip_safe=False,
python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*",
install_requires=[
"python-dateutil>=2.7.0",
"backports.functools_lru_cache>=1.2.1;python_version=='2.7'",
],
classifiers=[
"Development Status :: 4 - Beta",
"Intended Audience :: Developers",
"License :: OSI Approved :: Apache Software License",
"Topic :: Software Development :: Libraries :: Python Modules",
"Programming Language :: Python :: 2",
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
],
keywords="arrow date time datetime timestamp timezone humanize",
project_urls={
"Repository": "https://github.com/arrow-py/arrow",
"Bug Reports": "https://github.com/arrow-py/arrow/issues",
"Documentation": "https://arrow.readthedocs.io",
},
)

Some files were not shown because too many files have changed in this diff Show more