pax_global_header 0000666 0000000 0000000 00000000064 14151001236 0014503 g ustar 00root root 0000000 0000000 52 comment=d872459ff338f76e4f7a9bef67607054656822cc
python-arrow-1.2.1/ 0000775 0000000 0000000 00000000000 14151001236 0014155 5 ustar 00root root 0000000 0000000 python-arrow-1.2.1/.github/ 0000775 0000000 0000000 00000000000 14151001236 0015515 5 ustar 00root root 0000000 0000000 python-arrow-1.2.1/.github/FUNDING.yml 0000664 0000000 0000000 00000000027 14151001236 0017331 0 ustar 00root root 0000000 0000000 open_collective: arrow
python-arrow-1.2.1/.github/ISSUE_TEMPLATE/ 0000775 0000000 0000000 00000000000 14151001236 0017700 5 ustar 00root root 0000000 0000000 python-arrow-1.2.1/.github/ISSUE_TEMPLATE/bug_report.md 0000664 0000000 0000000 00000001357 14151001236 0022400 0 ustar 00root root 0000000 0000000 ---
name: "🐞 Bug Report"
about: Find a bug? Create a report to help us improve.
title: ''
labels: 'bug'
assignees: ''
---
## Issue Description
## System Info
- 🖥 **OS name and version**:
- 🐍 **Python version**:
- 🏹 **Arrow version**:
python-arrow-1.2.1/.github/ISSUE_TEMPLATE/documentation.md 0000664 0000000 0000000 00000000633 14151001236 0023075 0 ustar 00root root 0000000 0000000 ---
name: "📚 Documentation"
about: Find errors or problems in the docs (https://arrow.readthedocs.io)?
title: ''
labels: 'documentation'
assignees: ''
---
## Issue Description
python-arrow-1.2.1/.github/ISSUE_TEMPLATE/feature_request.md 0000664 0000000 0000000 00000000606 14151001236 0023427 0 ustar 00root root 0000000 0000000 ---
name: "💡 Feature Request"
about: Have an idea for a new feature or improvement?
title: ''
labels: 'enhancement'
assignees: ''
---
## Feature Request
python-arrow-1.2.1/.github/pull_request_template.md 0000664 0000000 0000000 00000002155 14151001236 0022461 0 ustar 00root root 0000000 0000000 ## Pull Request Checklist
Thank you for taking the time to improve Arrow! Before submitting your pull request, please check all *appropriate* boxes:
- [ ] 🧪 Added **tests** for changed code.
- [ ] 🛠️ All tests **pass** when run locally (run `tox` or `make test` to find out!).
- [ ] 🧹 All linting checks **pass** when run locally (run `tox -e lint` or `make lint` to find out!).
- [ ] 📚 Updated **documentation** for changed code.
- [ ] ⏩ Code is **up-to-date** with the `master` branch.
If you have *any* questions about your code changes or any of the points above, please submit your questions along with the pull request and we will try our best to help!
## Description of Changes
python-arrow-1.2.1/.github/workflows/ 0000775 0000000 0000000 00000000000 14151001236 0017552 5 ustar 00root root 0000000 0000000 python-arrow-1.2.1/.github/workflows/continuous_integration.yml 0000664 0000000 0000000 00000006336 14151001236 0025116 0 ustar 00root root 0000000 0000000 name: tests
on:
pull_request: # Run on all pull requests
push: # Run only on pushes to master
branches:
- master
schedule: # Run monthly
- cron: "0 0 1 * *"
jobs:
test:
name: ${{ matrix.os }} (${{ matrix.python-version }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
python-version: ["pypy-3.7", "3.6", "3.7", "3.8", "3.9", "3.10"]
os: [ubuntu-latest, macos-latest, windows-latest]
exclude:
# pypy3 randomly fails on Windows builds
- os: windows-latest
python-version: "pypy-3.7"
steps:
# Check out latest code
- uses: actions/checkout@v2
# Configure pip cache
- name: Cache pip (Linux)
uses: actions/cache@v2
if: startsWith(runner.os, 'Linux')
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements-dev.txt') }}
restore-keys: |
${{ runner.os }}-pip-
- name: Cache pip (macOS)
uses: actions/cache@v2
if: startsWith(runner.os, 'macOS')
with:
path: ~/Library/Caches/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements-dev.txt') }}
restore-keys: |
${{ runner.os }}-pip-
- name: Cache pip (Windows)
uses: actions/cache@v2
if: startsWith(runner.os, 'Windows')
with:
path: ~\AppData\Local\pip\Cache
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements-dev.txt') }}
restore-keys: |
${{ runner.os }}-pip-
# Set up Python
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
# Install dependencies
- name: Install dependencies
run: |
pip install -U pip setuptools wheel
pip install -U tox tox-gh-actions
# Run tests
- name: Test with tox
run: tox
# Upload coverage report
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v2
with:
file: coverage.xml
lint:
runs-on: ubuntu-latest
steps:
# Check out latest code
- uses: actions/checkout@v2
# Set up Python
- name: Set up Python 3.10
uses: actions/setup-python@v2
with:
python-version: "3.10"
# Configure pip cache
- name: Cache pip
uses: actions/cache@v2
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements-dev.txt') }}
restore-keys: |
${{ runner.os }}-pip-
# Configure pre-commit cache
- name: Cache pre-commit
uses: actions/cache@v2
with:
path: ~/.cache/pre-commit
key: ${{ runner.os }}-pre-commit-${{ hashFiles('**/.pre-commit-config.yaml') }}
restore-keys: |
${{ runner.os }}-pre-commit-
# Install dependencies
- name: Install dependencies
run: |
pip install -U pip setuptools wheel
pip install -U tox
# Lint code
- name: Lint code
run: tox -e lint
# Lint docs
- name: Lint docs
run: tox -e docs
python-arrow-1.2.1/.pre-commit-config.yaml 0000664 0000000 0000000 00000002567 14151001236 0020450 0 ustar 00root root 0000000 0000000 default_language_version:
python: python3
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.0.1
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: fix-encoding-pragma
args: [--remove]
- 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.9.3
hooks:
- id: isort
- repo: https://github.com/asottile/pyupgrade
rev: v2.29.0
hooks:
- id: pyupgrade
args: [--py36-plus]
- repo: https://github.com/pre-commit/pygrep-hooks
rev: v1.9.0
hooks:
- id: python-no-eval
- id: python-check-blanket-noqa
- id: python-use-type-annotations
- id: rst-backticks
- id: rst-directive-colons
- id: rst-inline-touching-normal
- repo: https://github.com/psf/black
rev: 21.9b0
hooks:
- id: black
args: [--safe, --quiet, --target-version=py36]
- repo: https://github.com/pycqa/flake8
rev: 4.0.1
hooks:
- id: flake8
additional_dependencies: [flake8-bugbear]
- repo: https://github.com/pre-commit/mirrors-mypy
rev: 'v0.910-1'
hooks:
- id: mypy
additional_dependencies: [types-python-dateutil]
python-arrow-1.2.1/CHANGELOG.rst 0000664 0000000 0000000 00000075262 14151001236 0016212 0 ustar 00root root 0000000 0000000 Changelog
=========
1.2.1 (2021-10-24)
------------------
- [NEW] Added quarter granularity to humanize, for example:
.. code-block:: python
>>> import arrow
>>> now = arrow.now()
>>> four_month_shift = now.shift(months=4)
>>> now.humanize(four_month_shift, granularity="quarter")
'a quarter ago'
>>> four_month_shift.humanize(now, granularity="quarter")
'in a quarter'
>>> thirteen_month_shift = now.shift(months=13)
>>> thirteen_month_shift.humanize(now, granularity="quarter")
'in 4 quarters'
>>> now.humanize(thirteen_month_shift, granularity="quarter")
'4 quarters ago'
- [NEW] Added Sinhala and Urdu locales.
- [NEW] Added official support for Python 3.10.
- [CHANGED] Updated Azerbaijani, Hebrew, and Serbian locales and added tests.
- [CHANGED] Passing an empty granularity list to ``humanize`` now raises a ``ValueError``.
1.2.0 (2021-09-12)
------------------
- [NEW] Added Albanian, Tamil and Zulu locales.
- [NEW] Added support for ``Decimal`` as input to ``arrow.get()``.
- [FIX] The Estonian, Finnish, Nepali and Zulu locales now support ``dehumanize``.
- [FIX] Improved validation checks when using parser tokens ``A`` and ``hh``.
- [FIX] Minor bug fixes to Catalan, Cantonese, Greek and Nepali locales.
1.1.1 (2021-06-24)
------------------
- [NEW] Added Odia, Maltese, Serbian, Sami, and Luxembourgish locales.
- [FIXED] All calls to ``arrow.get()`` should now properly pass the ``tzinfo`` argument to the Arrow constructor. See PR `#968 `_ for more info.
- [FIXED] Humanize output is now properly truncated when a locale class overrides ``_format_timeframe()``.
- [CHANGED] Renamed ``requirements.txt`` to ``requirements-dev.txt`` to prevent confusion with the dependencies in ``setup.py``.
- [CHANGED] Updated Turkish locale and added tests.
1.1.0 (2021-04-26)
------------------
- [NEW] Implemented the ``dehumanize`` method for ``Arrow`` objects. This takes human readable input and uses it to perform relative time shifts, for example:
.. code-block:: python
>>> arw
>>> arw.dehumanize("8 hours ago")
>>> arw.dehumanize("in 4 days")
>>> arw.dehumanize("in an hour 34 minutes 10 seconds")
>>> arw.dehumanize("hace 2 años", locale="es")
- [NEW] Made the start of the week adjustable when using ``span("week")``, for example:
.. code-block:: python
>>> arw
>>> arw.isoweekday()
1 # Monday
>>> arw.span("week")
(, )
>>> arw.span("week", week_start=4)
(, )
- [NEW] Added Croatian, Latin, Latvian, Lithuanian and Malay locales.
- [FIX] Internally standardize locales and improve locale validation. Locales should now use the ISO notation of a dash (``"en-gb"``) rather than an underscore (``"en_gb"``) however this change is backward compatible.
- [FIX] Correct type checking for internal locale mapping by using ``_init_subclass``. This now allows subclassing of locales, for example:
.. code-block:: python
>>> from arrow.locales import EnglishLocale
>>> class Klingon(EnglishLocale):
... names = ["tlh"]
...
>>> from arrow import locales
>>> locales.get_locale("tlh")
<__main__.Klingon object at 0x7f7cd1effd30>
- [FIX] Correct type checking for ``arrow.get(2021, 3, 9)`` construction.
- [FIX] Audited all docstrings for style, typos and outdated info.
1.0.3 (2021-03-05)
------------------
- [FIX] Updated internals to avoid issues when running ``mypy --strict``.
- [FIX] Corrections to Swedish locale.
- [INTERNAL] Lowered required coverage limit until ``humanize`` month tests are fixed.
1.0.2 (2021-02-28)
------------------
- [FIXED] Fixed an ``OverflowError`` that could occur when running Arrow on a 32-bit OS.
1.0.1 (2021-02-27)
------------------
- [FIXED] A ``py.typed`` file is now bundled with the Arrow package to conform to PEP 561.
1.0.0 (2021-02-26)
------------------
After 8 years we're pleased to announce Arrow v1.0. Thanks to the entire Python community for helping make Arrow the amazing package it is today!
- [CHANGE] Arrow has **dropped support** for Python 2.7 and 3.5.
- [CHANGE] There are multiple **breaking changes** with this release, please see the `migration guide `_ for a complete overview.
- [CHANGE] Arrow is now following `semantic versioning `_.
- [CHANGE] Made ``humanize`` granularity="auto" limits more accurate to reduce strange results.
- [NEW] Added support for Python 3.9.
- [NEW] Added a new keyword argument "exact" to ``span``, ``span_range`` and ``interval`` methods. This makes timespans begin at the start time given and not extend beyond the end time given, for example:
.. code-block:: python
>>> start = Arrow(2021, 2, 5, 12, 30)
>>> end = Arrow(2021, 2, 5, 17, 15)
>>> for r in arrow.Arrow.span_range('hour', start, end, exact=True):
... print(r)
...
(, )
(, )
(, )
(, )
(, )
- [NEW] Arrow now natively supports PEP 484-style type annotations.
- [FIX] Fixed handling of maximum permitted timestamp on Windows systems.
- [FIX] Corrections to French, German, Japanese and Norwegian locales.
- [INTERNAL] Raise more appropriate errors when string parsing fails to match.
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)
.. 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)
...
(, )
(, )
(, )
(, )
(, )
- [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 `_ 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')
>>> before.fold
0
>>> before.ambiguous
True
>>> after = Arrow(2017, 10, 29, 2, 0, tzinfo='Europe/Stockholm', fold=1)
>>> after = before.replace(fold=1)
- [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.get("2013-036 \t 04:05:06Z", normalize_whitespace=True)
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 `_.
- [INTERNAL] Moved arrow to an organization. The repo can now be found `here `_.
- [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 `_ 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)
>>> 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 `_ via a new token ``W``, for example:
.. code-block:: python
>>> arrow.get("2013-W29-6", "W")
>>> utc=arrow.utcnow()
>>> utc
>>> 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 `_.
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 `_).
- [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'))
- [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))
- [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 `_ 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 `_ 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 `_.
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 `_ 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 `_, `#196 `_, `#396 `_, `#434 `_, `#447 `_, `#456 `_, `#519 `_, `#538 `_, `#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 Portuguese 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 `_.
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 `_ 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
python-arrow-1.2.1/LICENSE 0000664 0000000 0000000 00000026115 14151001236 0015167 0 ustar 00root root 0000000 0000000 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 2021 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.
python-arrow-1.2.1/MANIFEST.in 0000664 0000000 0000000 00000000235 14151001236 0015713 0 ustar 00root root 0000000 0000000 include LICENSE CHANGELOG.rst README.rst Makefile requirements-dev.txt tox.ini
recursive-include tests *.py
recursive-include docs *.py *.rst *.bat Makefile
python-arrow-1.2.1/Makefile 0000664 0000000 0000000 00000002010 14151001236 0015606 0 ustar 00root root 0000000 0000000 .PHONY: auto test docs clean
auto: build39
build36: PYTHON_VER = python3.6
build37: PYTHON_VER = python3.7
build38: PYTHON_VER = python3.8
build39: PYTHON_VER = python3.9
build310: PYTHON_VER = python3.10
build36 build37 build38 build39 build310: clean
$(PYTHON_VER) -m venv venv
. venv/bin/activate; \
pip install -U pip setuptools wheel; \
pip install -r requirements-dev.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 pip 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
python-arrow-1.2.1/README.rst 0000664 0000000 0000000 00000013122 14151001236 0015643 0 ustar 00root root 0000000 0000000 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 `_ and is heavily inspired by `moment.js `_ and `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
- Support for Python 3.6+
- Timezone-aware and UTC by default
- Super-simple creation options for many common input scenarios
- ``shift`` method with support for relative offsets, including weeks
- Format and parse strings automatically
- Wide support for the `ISO 8601 `_ standard
- Timezone conversion
- Support for ``dateutil``, ``pytz``, and ``ZoneInfo`` tzinfo objects
- Generates time spans, ranges, floors and ceilings for time frames ranging from microsecond to year
- Humanize dates and times with a growing list of contributed locales
- Extensible for your own Arrow-derived types
- Full support for PEP 484-style type hints
Quick Start
-----------
Installation
~~~~~~~~~~~~
To install Arrow, use `pip `_ or `pipenv `_:
.. code-block:: console
$ pip install -U arrow
Example Usage
~~~~~~~~~~~~~
.. code-block:: python
>>> import arrow
>>> arrow.get('2013-05-11T21:23:58.970460+07:00')
>>> utc = arrow.utcnow()
>>> utc
>>> utc = utc.shift(hours=-1)
>>> utc
>>> local = utc.to('US/Pacific')
>>> local
>>> local.timestamp()
1368303838.970460
>>> 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')
'한시간 전'
.. end-inclusion-marker-do-not-remove
Documentation
-------------
For full documentation, please visit `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 `_. Issues marked with the `"good first issue" label `_ may be a great place to start!
#. Fork `this repository `_ 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: ``tox && tox -e lint,docs`` (if you have `tox `_ installed) **OR** ``make build39 && make test && make lint`` (if you do not have Python 3.9 installed, replace ``build39`` 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 `_.
Support Arrow
-------------
`Open Collective `_ 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 `_.
python-arrow-1.2.1/arrow/ 0000775 0000000 0000000 00000000000 14151001236 0015307 5 ustar 00root root 0000000 0000000 python-arrow-1.2.1/arrow/__init__.py 0000664 0000000 0000000 00000001550 14151001236 0017421 0 ustar 00root root 0000000 0000000 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
# https://mypy.readthedocs.io/en/stable/command_line.html#cmdoption-mypy-no-implicit-reexport
# Mypy with --strict or --no-implicit-reexport requires an explicit reexport.
__all__ = [
"__version__",
"get",
"now",
"utcnow",
"Arrow",
"ArrowFactory",
"FORMAT_ATOM",
"FORMAT_COOKIE",
"FORMAT_RFC822",
"FORMAT_RFC850",
"FORMAT_RFC1036",
"FORMAT_RFC1123",
"FORMAT_RFC2822",
"FORMAT_RFC3339",
"FORMAT_RSS",
"FORMAT_W3C",
"ParserError",
]
python-arrow-1.2.1/arrow/_version.py 0000664 0000000 0000000 00000000026 14151001236 0017503 0 ustar 00root root 0000000 0000000 __version__ = "1.2.1"
python-arrow-1.2.1/arrow/api.py 0000664 0000000 0000000 00000005303 14151001236 0016433 0 ustar 00root root 0000000 0000000 """
Provides the default implementation of :class:`ArrowFactory `
methods for use as a module API.
"""
from datetime import date, datetime
from datetime import tzinfo as dt_tzinfo
from time import struct_time
from typing import Any, List, Optional, Tuple, Type, Union, overload
from arrow.arrow import TZ_EXPR, Arrow
from arrow.constants import DEFAULT_LOCALE
from arrow.factory import ArrowFactory
# internal default factory.
_factory = ArrowFactory()
# TODO: Use Positional Only Argument (https://www.python.org/dev/peps/pep-0570/)
# after Python 3.7 deprecation
@overload
def get(
*,
locale: str = DEFAULT_LOCALE,
tzinfo: Optional[TZ_EXPR] = None,
normalize_whitespace: bool = False,
) -> Arrow:
... # pragma: no cover
@overload
def get(
*args: int,
locale: str = DEFAULT_LOCALE,
tzinfo: Optional[TZ_EXPR] = None,
normalize_whitespace: bool = False,
) -> Arrow:
... # pragma: no cover
@overload
def get(
__obj: Union[
Arrow,
datetime,
date,
struct_time,
dt_tzinfo,
int,
float,
str,
Tuple[int, int, int],
],
*,
locale: str = DEFAULT_LOCALE,
tzinfo: Optional[TZ_EXPR] = None,
normalize_whitespace: bool = False,
) -> Arrow:
... # pragma: no cover
@overload
def get(
__arg1: Union[datetime, date],
__arg2: TZ_EXPR,
*,
locale: str = DEFAULT_LOCALE,
tzinfo: Optional[TZ_EXPR] = None,
normalize_whitespace: bool = False,
) -> Arrow:
... # pragma: no cover
@overload
def get(
__arg1: str,
__arg2: Union[str, List[str]],
*,
locale: str = DEFAULT_LOCALE,
tzinfo: Optional[TZ_EXPR] = None,
normalize_whitespace: bool = False,
) -> Arrow:
... # pragma: no cover
def get(*args: Any, **kwargs: Any) -> Arrow:
"""Calls the default :class:`ArrowFactory ` ``get`` method."""
return _factory.get(*args, **kwargs)
get.__doc__ = _factory.get.__doc__
def utcnow() -> Arrow:
"""Calls the default :class:`ArrowFactory ` ``utcnow`` method."""
return _factory.utcnow()
utcnow.__doc__ = _factory.utcnow.__doc__
def now(tz: Optional[TZ_EXPR] = None) -> Arrow:
"""Calls the default :class:`ArrowFactory ` ``now`` method."""
return _factory.now(tz)
now.__doc__ = _factory.now.__doc__
def factory(type: Type[Arrow]) -> ArrowFactory:
"""Returns an :class:`.ArrowFactory` for the specified :class:`Arrow `
or derived type.
:param type: the type, :class:`Arrow ` or derived.
"""
return ArrowFactory(type)
__all__ = ["get", "utcnow", "now", "factory"]
python-arrow-1.2.1/arrow/arrow.py 0000664 0000000 0000000 00000174110 14151001236 0017017 0 ustar 00root root 0000000 0000000 """
Provides the :class:`Arrow ` class, an enhanced ``datetime``
replacement.
"""
import calendar
import re
import sys
from datetime import date
from datetime import datetime as dt_datetime
from datetime import time as dt_time
from datetime import timedelta
from datetime import tzinfo as dt_tzinfo
from math import trunc
from time import struct_time
from typing import (
Any,
ClassVar,
Generator,
Iterable,
List,
Mapping,
Optional,
Tuple,
Union,
cast,
overload,
)
from dateutil import tz as dateutil_tz
from dateutil.relativedelta import relativedelta
from arrow import formatter, locales, parser, util
from arrow.constants import DEFAULT_LOCALE, DEHUMANIZE_LOCALES
from arrow.locales import TimeFrameLiteral
if sys.version_info < (3, 8): # pragma: no cover
from typing_extensions import Final, Literal
else:
from typing import Final, Literal # pragma: no cover
TZ_EXPR = Union[dt_tzinfo, str]
_T_FRAMES = Literal[
"year",
"years",
"month",
"months",
"day",
"days",
"hour",
"hours",
"minute",
"minutes",
"second",
"seconds",
"microsecond",
"microseconds",
"week",
"weeks",
"quarter",
"quarters",
]
_BOUNDS = Literal["[)", "()", "(]", "[]"]
_GRANULARITY = Literal[
"auto",
"second",
"minute",
"hour",
"day",
"week",
"month",
"quarter",
"year",
]
class Arrow:
"""An :class:`Arrow ` object.
Implements the ``datetime`` interface, behaving as an aware ``datetime`` while implementing
additional functionality.
:param year: the calendar year.
:param month: the calendar month.
:param day: the calendar day.
:param hour: (optional) the hour. Defaults to 0.
:param minute: (optional) the minute, Defaults to 0.
:param second: (optional) the second, Defaults to 0.
:param microsecond: (optional) the microsecond. Defaults to 0.
:param tzinfo: (optional) A timezone expression. Defaults to UTC.
:param fold: (optional) 0 or 1, used to disambiguate repeated wall times. Defaults to 0.
.. _tz-expr:
Recognized timezone expressions:
- A ``tzinfo`` object.
- A ``str`` describing a timezone, similar to 'US/Pacific', or 'Europe/Berlin'.
- A ``str`` in ISO 8601 style, as in '+07:00'.
- A ``str``, one of the following: 'local', 'utc', 'UTC'.
Usage::
>>> import arrow
>>> arrow.Arrow(2013, 5, 5, 12, 30, 45)
"""
resolution: ClassVar[timedelta] = dt_datetime.resolution
min: ClassVar["Arrow"]
max: ClassVar["Arrow"]
_ATTRS: Final[List[str]] = [
"year",
"month",
"day",
"hour",
"minute",
"second",
"microsecond",
]
_ATTRS_PLURAL: Final[List[str]] = [f"{a}s" for a in _ATTRS]
_MONTHS_PER_QUARTER: Final[int] = 3
_SECS_PER_MINUTE: Final[int] = 60
_SECS_PER_HOUR: Final[int] = 60 * 60
_SECS_PER_DAY: Final[int] = 60 * 60 * 24
_SECS_PER_WEEK: Final[int] = 60 * 60 * 24 * 7
_SECS_PER_MONTH: Final[float] = 60 * 60 * 24 * 30.5
_SECS_PER_QUARTER: Final[float] = 60 * 60 * 24 * 30.5 * 3
_SECS_PER_YEAR: Final[int] = 60 * 60 * 24 * 365
_SECS_MAP: Final[Mapping[TimeFrameLiteral, float]] = {
"second": 1.0,
"minute": _SECS_PER_MINUTE,
"hour": _SECS_PER_HOUR,
"day": _SECS_PER_DAY,
"week": _SECS_PER_WEEK,
"month": _SECS_PER_MONTH,
"quarter": _SECS_PER_QUARTER,
"year": _SECS_PER_YEAR,
}
_datetime: dt_datetime
def __init__(
self,
year: int,
month: int,
day: int,
hour: int = 0,
minute: int = 0,
second: int = 0,
microsecond: int = 0,
tzinfo: Optional[TZ_EXPR] = None,
**kwargs: Any,
) -> None:
if tzinfo is None:
tzinfo = dateutil_tz.tzutc()
# detect that tzinfo is a pytz object (issue #626)
elif (
isinstance(tzinfo, dt_tzinfo)
and hasattr(tzinfo, "localize")
and hasattr(tzinfo, "zone")
and tzinfo.zone # type: ignore[attr-defined]
):
tzinfo = parser.TzinfoParser.parse(tzinfo.zone) # type: ignore[attr-defined]
elif isinstance(tzinfo, str):
tzinfo = parser.TzinfoParser.parse(tzinfo)
fold = kwargs.get("fold", 0)
self._datetime = dt_datetime(
year, month, day, hour, minute, second, microsecond, tzinfo, fold=fold
)
# factories: single object, both original and from datetime.
@classmethod
def now(cls, tzinfo: Optional[dt_tzinfo] = None) -> "Arrow":
"""Constructs an :class:`Arrow ` object, representing "now" in the given
timezone.
:param tzinfo: (optional) a ``tzinfo`` object. Defaults to local time.
Usage::
>>> arrow.now('Asia/Baku')
"""
if tzinfo is None:
tzinfo = dateutil_tz.tzlocal()
dt = dt_datetime.now(tzinfo)
return cls(
dt.year,
dt.month,
dt.day,
dt.hour,
dt.minute,
dt.second,
dt.microsecond,
dt.tzinfo,
fold=getattr(dt, "fold", 0),
)
@classmethod
def utcnow(cls) -> "Arrow":
"""Constructs an :class:`Arrow ` object, representing "now" in UTC
time.
Usage::
>>> arrow.utcnow()
"""
dt = dt_datetime.now(dateutil_tz.tzutc())
return cls(
dt.year,
dt.month,
dt.day,
dt.hour,
dt.minute,
dt.second,
dt.microsecond,
dt.tzinfo,
fold=getattr(dt, "fold", 0),
)
@classmethod
def fromtimestamp(
cls,
timestamp: Union[int, float, str],
tzinfo: Optional[TZ_EXPR] = None,
) -> "Arrow":
"""Constructs an :class:`Arrow ` object from a timestamp, converted to
the given timezone.
:param timestamp: an ``int`` or ``float`` timestamp, or a ``str`` that converts to either.
:param tzinfo: (optional) a ``tzinfo`` object. Defaults to local time.
"""
if tzinfo is None:
tzinfo = dateutil_tz.tzlocal()
elif isinstance(tzinfo, str):
tzinfo = parser.TzinfoParser.parse(tzinfo)
if not util.is_timestamp(timestamp):
raise ValueError(f"The provided timestamp {timestamp!r} is invalid.")
timestamp = util.normalize_timestamp(float(timestamp))
dt = dt_datetime.fromtimestamp(timestamp, tzinfo)
return cls(
dt.year,
dt.month,
dt.day,
dt.hour,
dt.minute,
dt.second,
dt.microsecond,
dt.tzinfo,
fold=getattr(dt, "fold", 0),
)
@classmethod
def utcfromtimestamp(cls, timestamp: Union[int, float, str]) -> "Arrow":
"""Constructs an :class:`Arrow ` object from a timestamp, in UTC time.
:param timestamp: an ``int`` or ``float`` timestamp, or a ``str`` that converts to either.
"""
if not util.is_timestamp(timestamp):
raise ValueError(f"The provided timestamp {timestamp!r} is invalid.")
timestamp = util.normalize_timestamp(float(timestamp))
dt = dt_datetime.utcfromtimestamp(timestamp)
return cls(
dt.year,
dt.month,
dt.day,
dt.hour,
dt.minute,
dt.second,
dt.microsecond,
dateutil_tz.tzutc(),
fold=getattr(dt, "fold", 0),
)
@classmethod
def fromdatetime(cls, dt: dt_datetime, tzinfo: Optional[TZ_EXPR] = None) -> "Arrow":
"""Constructs an :class:`Arrow ` object from a ``datetime`` and
optional replacement timezone.
:param dt: the ``datetime``
:param tzinfo: (optional) A :ref:`timezone expression `. Defaults to ``dt``'s
timezone, or UTC if naive.
Usage::
>>> dt
datetime.datetime(2021, 4, 7, 13, 48, tzinfo=tzfile('/usr/share/zoneinfo/US/Pacific'))
>>> arrow.Arrow.fromdatetime(dt)
"""
if tzinfo is None:
if dt.tzinfo is None:
tzinfo = dateutil_tz.tzutc()
else:
tzinfo = dt.tzinfo
return cls(
dt.year,
dt.month,
dt.day,
dt.hour,
dt.minute,
dt.second,
dt.microsecond,
tzinfo,
fold=getattr(dt, "fold", 0),
)
@classmethod
def fromdate(cls, date: date, tzinfo: Optional[TZ_EXPR] = None) -> "Arrow":
"""Constructs an :class:`Arrow ` object from a ``date`` and optional
replacement timezone. All time values are set to 0.
:param date: the ``date``
:param tzinfo: (optional) A :ref:`timezone expression `. Defaults to UTC.
"""
if tzinfo is None:
tzinfo = dateutil_tz.tzutc()
return cls(date.year, date.month, date.day, tzinfo=tzinfo)
@classmethod
def strptime(
cls, date_str: str, fmt: str, tzinfo: Optional[TZ_EXPR] = None
) -> "Arrow":
"""Constructs an :class:`Arrow ` object from a date string and format,
in the style of ``datetime.strptime``. Optionally replaces the parsed timezone.
:param date_str: the date string.
:param fmt: the format string using datetime format codes.
:param tzinfo: (optional) A :ref:`timezone expression `. Defaults to the parsed
timezone if ``fmt`` contains a timezone directive, otherwise UTC.
Usage::
>>> arrow.Arrow.strptime('20-01-2019 15:49:10', '%d-%m-%Y %H:%M:%S')
"""
dt = dt_datetime.strptime(date_str, fmt)
if tzinfo is None:
tzinfo = dt.tzinfo
return cls(
dt.year,
dt.month,
dt.day,
dt.hour,
dt.minute,
dt.second,
dt.microsecond,
tzinfo,
fold=getattr(dt, "fold", 0),
)
@classmethod
def fromordinal(cls, ordinal: int) -> "Arrow":
"""Constructs an :class:`Arrow ` object corresponding
to the Gregorian Ordinal.
:param ordinal: an ``int`` corresponding to a Gregorian Ordinal.
Usage::
>>> arrow.fromordinal(737741)
"""
util.validate_ordinal(ordinal)
dt = dt_datetime.fromordinal(ordinal)
return cls(
dt.year,
dt.month,
dt.day,
dt.hour,
dt.minute,
dt.second,
dt.microsecond,
dt.tzinfo,
fold=getattr(dt, "fold", 0),
)
# factories: ranges and spans
@classmethod
def range(
cls,
frame: _T_FRAMES,
start: Union["Arrow", dt_datetime],
end: Union["Arrow", dt_datetime, None] = None,
tz: Optional[TZ_EXPR] = None,
limit: Optional[int] = None,
) -> Generator["Arrow", None, None]:
"""Returns an iterator of :class:`Arrow ` objects, representing
points in time between two inputs.
:param frame: The timeframe. Can be any ``datetime`` property (day, hour, minute...).
:param start: A datetime expression, the start of the range.
:param end: (optional) A datetime expression, the end of the range.
:param tz: (optional) A :ref:`timezone expression `. Defaults to
``start``'s timezone, or UTC if ``start`` is naive.
:param limit: (optional) A maximum number of tuples to return.
**NOTE**: The ``end`` or ``limit`` must be provided. Call with ``end`` alone to
return the entire range. Call with ``limit`` alone to return a maximum # of results from
the start. Call with both to cap a range at a maximum # of results.
**NOTE**: ``tz`` internally **replaces** the timezones of both ``start`` and ``end`` before
iterating. As such, either call with naive objects and ``tz``, or aware objects from the
same timezone and no ``tz``.
Supported frame values: year, quarter, month, week, day, hour, minute, second, microsecond.
Recognized datetime expressions:
- An :class:`Arrow ` object.
- A ``datetime`` object.
Usage::
>>> 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))
...
**NOTE**: Unlike Python's ``range``, ``end`` *may* be included in the returned iterator::
>>> start = datetime(2013, 5, 5, 12, 30)
>>> end = datetime(2013, 5, 5, 13, 30)
>>> for r in arrow.Arrow.range('hour', start, end):
... print(repr(r))
...
"""
_, frame_relative, relative_steps = cls._get_frames(frame)
tzinfo = cls._get_tzinfo(start.tzinfo if tz is None else tz)
start = cls._get_datetime(start).replace(tzinfo=tzinfo)
end, limit = cls._get_iteration_params(end, limit)
end = cls._get_datetime(end).replace(tzinfo=tzinfo)
current = cls.fromdatetime(start)
original_day = start.day
day_is_clipped = False
i = 0
while current <= end and i < limit:
i += 1
yield current
values = [getattr(current, f) for f in cls._ATTRS]
current = cls(*values, tzinfo=tzinfo).shift( # type: ignore
**{frame_relative: relative_steps}
)
if frame in ["month", "quarter", "year"] and current.day < original_day:
day_is_clipped = True
if day_is_clipped and not cls._is_last_day_of_month(current):
current = current.replace(day=original_day)
def span(
self,
frame: _T_FRAMES,
count: int = 1,
bounds: _BOUNDS = "[)",
exact: bool = False,
week_start: int = 1,
) -> Tuple["Arrow", "Arrow"]:
"""Returns a tuple of two new :class:`Arrow ` objects, representing the timespan
of the :class:`Arrow ` object in a given timeframe.
:param frame: the timeframe. Can be any ``datetime`` property (day, hour, minute...).
:param count: (optional) the number of frames to span.
:param bounds: (optional) a ``str`` of either '()', '(]', '[)', or '[]' that specifies
whether to include or exclude the start and end values in the span. '(' excludes
the start, '[' includes the start, ')' excludes the end, and ']' includes the end.
If the bounds are not specified, the default bound '[)' is used.
:param exact: (optional) whether to have the start of the timespan begin exactly
at the time specified by ``start`` and the end of the timespan truncated
so as not to extend beyond ``end``.
:param week_start: (optional) only used in combination with the week timeframe. Follows isoweekday() where
Monday is 1 and Sunday is 7.
Supported frame values: year, quarter, month, week, day, hour, minute, second.
Usage::
>>> arrow.utcnow()
>>> arrow.utcnow().span('hour')
(, )
>>> arrow.utcnow().span('day')
(, )
>>> arrow.utcnow().span('day', count=2)
(, )
>>> arrow.utcnow().span('day', bounds='[]')
(, )
>>> arrow.utcnow().span('week')
(, )
>>> arrow.utcnow().span('week', week_start=6)
(, )
"""
if not 1 <= week_start <= 7:
raise ValueError("week_start argument must be between 1 and 7.")
util.validate_bounds(bounds)
frame_absolute, frame_relative, relative_steps = self._get_frames(frame)
if frame_absolute == "week":
attr = "day"
elif frame_absolute == "quarter":
attr = "month"
else:
attr = frame_absolute
floor = self
if not exact:
index = self._ATTRS.index(attr)
frames = self._ATTRS[: index + 1]
values = [getattr(self, f) for f in frames]
for _ in range(3 - len(values)):
values.append(1)
floor = self.__class__(*values, tzinfo=self.tzinfo) # type: ignore
if frame_absolute == "week":
# if week_start is greater than self.isoweekday() go back one week by setting delta = 7
delta = 7 if week_start > self.isoweekday() else 0
floor = floor.shift(days=-(self.isoweekday() - week_start) - delta)
elif frame_absolute == "quarter":
floor = floor.shift(months=-((self.month - 1) % 3))
ceil = floor.shift(**{frame_relative: count * relative_steps})
if bounds[0] == "(":
floor = floor.shift(microseconds=+1)
if bounds[1] == ")":
ceil = ceil.shift(microseconds=-1)
return floor, ceil
def floor(self, frame: _T_FRAMES) -> "Arrow":
"""Returns a new :class:`Arrow ` object, representing the "floor"
of the timespan of the :class:`Arrow ` object in a given timeframe.
Equivalent to the first element in the 2-tuple returned by
:func:`span `.
:param frame: the timeframe. Can be any ``datetime`` property (day, hour, minute...).
Usage::
>>> arrow.utcnow().floor('hour')
"""
return self.span(frame)[0]
def ceil(self, frame: _T_FRAMES) -> "Arrow":
"""Returns a new :class:`Arrow ` object, representing the "ceiling"
of the timespan of the :class:`Arrow ` object in a given timeframe.
Equivalent to the second element in the 2-tuple returned by
:func:`span `.
:param frame: the timeframe. Can be any ``datetime`` property (day, hour, minute...).
Usage::
>>> arrow.utcnow().ceil('hour')
"""
return self.span(frame)[1]
@classmethod
def span_range(
cls,
frame: _T_FRAMES,
start: dt_datetime,
end: dt_datetime,
tz: Optional[TZ_EXPR] = None,
limit: Optional[int] = None,
bounds: _BOUNDS = "[)",
exact: bool = False,
) -> Iterable[Tuple["Arrow", "Arrow"]]:
"""Returns an iterator of tuples, each :class:`Arrow ` objects,
representing a series of timespans between two inputs.
:param frame: The timeframe. Can be any ``datetime`` property (day, hour, minute...).
:param start: A datetime expression, the start of the range.
:param end: (optional) A datetime expression, the end of the range.
:param tz: (optional) A :ref:`timezone expression `. Defaults to
``start``'s timezone, or UTC if ``start`` is naive.
:param limit: (optional) A maximum number of tuples to return.
:param bounds: (optional) a ``str`` of either '()', '(]', '[)', or '[]' that specifies
whether to include or exclude the start and end values in each span in the range. '(' excludes
the start, '[' includes the start, ')' excludes the end, and ']' includes the end.
If the bounds are not specified, the default bound '[)' is used.
:param exact: (optional) whether to have the first timespan start exactly
at the time specified by ``start`` and the final span truncated
so as not to extend beyond ``end``.
**NOTE**: The ``end`` or ``limit`` must be provided. Call with ``end`` alone to
return the entire range. Call with ``limit`` alone to return a maximum # of results from
the start. Call with both to cap a range at a maximum # of results.
**NOTE**: ``tz`` internally **replaces** the timezones of both ``start`` and ``end`` before
iterating. As such, either call with naive objects and ``tz``, or aware objects from the
same timezone and no ``tz``.
Supported frame values: year, quarter, month, week, day, hour, minute, second, microsecond.
Recognized datetime expressions:
- An :class:`Arrow ` object.
- A ``datetime`` object.
**NOTE**: Unlike Python's ``range``, ``end`` will *always* be included in the returned
iterator of timespans.
Usage:
>>> 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)
...
(, )
(, )
(, )
(, )
(, )
(, )
"""
tzinfo = cls._get_tzinfo(start.tzinfo if tz is None else tz)
start = cls.fromdatetime(start, tzinfo).span(frame, exact=exact)[0]
end = cls.fromdatetime(end, tzinfo)
_range = cls.range(frame, start, end, tz, limit)
if not exact:
for r in _range:
yield r.span(frame, bounds=bounds, exact=exact)
for r in _range:
floor, ceil = r.span(frame, bounds=bounds, exact=exact)
if ceil > end:
ceil = end
if bounds[1] == ")":
ceil += relativedelta(microseconds=-1)
if floor == end:
break
elif floor + relativedelta(microseconds=-1) == end:
break
yield floor, ceil
@classmethod
def interval(
cls,
frame: _T_FRAMES,
start: dt_datetime,
end: dt_datetime,
interval: int = 1,
tz: Optional[TZ_EXPR] = None,
bounds: _BOUNDS = "[)",
exact: bool = False,
) -> Iterable[Tuple["Arrow", "Arrow"]]:
"""Returns an iterator of tuples, each :class:`Arrow ` objects,
representing a series of intervals between two inputs.
:param frame: The timeframe. Can be any ``datetime`` property (day, hour, minute...).
:param start: A datetime expression, the start of the range.
:param end: (optional) A datetime expression, the end of the range.
:param interval: (optional) Time interval for the given time frame.
:param tz: (optional) A timezone expression. Defaults to UTC.
:param bounds: (optional) a ``str`` of either '()', '(]', '[)', or '[]' that specifies
whether to include or exclude the start and end values in the intervals. '(' excludes
the start, '[' includes the start, ')' excludes the end, and ']' includes the end.
If the bounds are not specified, the default bound '[)' is used.
:param exact: (optional) whether to have the first timespan start exactly
at the time specified by ``start`` and the final interval truncated
so as not to extend beyond ``end``.
Supported frame values: year, quarter, month, week, day, hour, minute, second
Recognized datetime expressions:
- An :class:`Arrow ` object.
- A ``datetime`` object.
Recognized timezone expressions:
- A ``tzinfo`` object.
- A ``str`` describing a timezone, similar to 'US/Pacific', or 'Europe/Berlin'.
- A ``str`` in ISO 8601 style, as in '+07:00'.
- A ``str``, one of the following: 'local', 'utc', 'UTC'.
Usage:
>>> start = datetime(2013, 5, 5, 12, 30)
>>> end = datetime(2013, 5, 5, 17, 15)
>>> for r in arrow.Arrow.interval('hour', start, end, 2):
... print r
...
(, )
(, )
(, )
"""
if interval < 1:
raise ValueError("interval has to be a positive integer")
spanRange = iter(
cls.span_range(frame, start, end, tz, bounds=bounds, exact=exact)
)
while True:
try:
intvlStart, intvlEnd = next(spanRange)
for _ in range(interval - 1):
try:
_, intvlEnd = next(spanRange)
except StopIteration:
continue
yield intvlStart, intvlEnd
except StopIteration:
return
# representations
def __repr__(self) -> str:
return f"<{self.__class__.__name__} [{self.__str__()}]>"
def __str__(self) -> str:
return self._datetime.isoformat()
def __format__(self, formatstr: str) -> str:
if len(formatstr) > 0:
return self.format(formatstr)
return str(self)
def __hash__(self) -> int:
return self._datetime.__hash__()
# attributes and properties
def __getattr__(self, name: str) -> int:
if name == "week":
return self.isocalendar()[1]
if name == "quarter":
return int((self.month - 1) / self._MONTHS_PER_QUARTER) + 1
if not name.startswith("_"):
value: Optional[int] = getattr(self._datetime, name, None)
if value is not None:
return value
return cast(int, object.__getattribute__(self, name))
@property
def tzinfo(self) -> dt_tzinfo:
"""Gets the ``tzinfo`` of the :class:`Arrow ` object.
Usage::
>>> arw=arrow.utcnow()
>>> arw.tzinfo
tzutc()
"""
# In Arrow, `_datetime` cannot be naive.
return cast(dt_tzinfo, self._datetime.tzinfo)
@property
def datetime(self) -> dt_datetime:
"""Returns a datetime representation of the :class:`Arrow ` object.
Usage::
>>> arw=arrow.utcnow()
>>> arw.datetime
datetime.datetime(2019, 1, 24, 16, 35, 27, 276649, tzinfo=tzutc())
"""
return self._datetime
@property
def naive(self) -> dt_datetime:
"""Returns a naive datetime representation of the :class:`Arrow `
object.
Usage::
>>> nairobi = arrow.now('Africa/Nairobi')
>>> nairobi
>>> nairobi.naive
datetime.datetime(2019, 1, 23, 19, 27, 12, 297999)
"""
return self._datetime.replace(tzinfo=None)
def timestamp(self) -> float:
"""Returns a timestamp representation of the :class:`Arrow ` object, in
UTC time.
Usage::
>>> arrow.utcnow().timestamp()
1616882340.256501
"""
return self._datetime.timestamp()
@property
def int_timestamp(self) -> int:
"""Returns an integer timestamp representation of the :class:`Arrow ` object, in
UTC time.
Usage::
>>> arrow.utcnow().int_timestamp
1548260567
"""
return int(self.timestamp())
@property
def float_timestamp(self) -> float:
"""Returns a floating-point timestamp representation of the :class:`Arrow `
object, in UTC time.
Usage::
>>> arrow.utcnow().float_timestamp
1548260516.830896
"""
return self.timestamp()
@property
def fold(self) -> int:
"""Returns the ``fold`` value of the :class:`Arrow ` object."""
return self._datetime.fold
@property
def ambiguous(self) -> bool:
"""Indicates whether the :class:`Arrow ` object is a repeated wall time in the current
timezone.
"""
return dateutil_tz.datetime_ambiguous(self._datetime)
@property
def imaginary(self) -> bool:
"""Indicates whether the :class: `Arrow ` object exists in the current timezone."""
return not dateutil_tz.datetime_exists(self._datetime)
# mutation and duplication.
def clone(self) -> "Arrow":
"""Returns a new :class:`Arrow ` object, cloned from the current one.
Usage:
>>> arw = arrow.utcnow()
>>> cloned = arw.clone()
"""
return self.fromdatetime(self._datetime)
def replace(self, **kwargs: Any) -> "Arrow":
"""Returns a new :class:`Arrow ` object with attributes updated
according to inputs.
Use property names to set their value absolutely::
>>> import arrow
>>> arw = arrow.utcnow()
>>> arw
>>> arw.replace(year=2014, month=6)
You can also replace the timezone without conversion, using a
:ref:`timezone expression `::
>>> arw.replace(tzinfo=tz.tzlocal())
"""
absolute_kwargs = {}
for key, value in kwargs.items():
if key in self._ATTRS:
absolute_kwargs[key] = value
elif key in ["week", "quarter"]:
raise ValueError(f"Setting absolute {key} is not supported.")
elif key not in ["tzinfo", "fold"]:
raise ValueError(f"Unknown attribute: {key!r}.")
current = self._datetime.replace(**absolute_kwargs)
tzinfo = kwargs.get("tzinfo")
if tzinfo is not None:
tzinfo = self._get_tzinfo(tzinfo)
current = current.replace(tzinfo=tzinfo)
fold = kwargs.get("fold")
if fold is not None:
current = current.replace(fold=fold)
return self.fromdatetime(current)
def shift(self, **kwargs: Any) -> "Arrow":
"""Returns a new :class:`Arrow ` object with attributes updated
according to inputs.
Use pluralized property names to relatively shift their current value:
>>> import arrow
>>> arw = arrow.utcnow()
>>> arw
>>> arw.shift(years=1, months=-1)
Day-of-the-week relative shifting can use either Python's weekday numbers
(Monday = 0, Tuesday = 1 .. Sunday = 6) or using dateutil.relativedelta's
day instances (MO, TU .. SU). When using weekday numbers, the returned
date will always be greater than or equal to the starting date.
Using the above code (which is a Saturday) and asking it to shift to Saturday:
>>> arw.shift(weekday=5)
While asking for a Monday:
>>> arw.shift(weekday=0)
"""
relative_kwargs = {}
additional_attrs = ["weeks", "quarters", "weekday"]
for key, value in kwargs.items():
if key in self._ATTRS_PLURAL or key in additional_attrs:
relative_kwargs[key] = value
else:
supported_attr = ", ".join(self._ATTRS_PLURAL + additional_attrs)
raise ValueError(
f"Invalid shift time frame. Please select one of the following: {supported_attr}."
)
# core datetime does not support quarters, translate to months.
relative_kwargs.setdefault("months", 0)
relative_kwargs["months"] += (
relative_kwargs.pop("quarters", 0) * self._MONTHS_PER_QUARTER
)
current = self._datetime + relativedelta(**relative_kwargs)
if not dateutil_tz.datetime_exists(current):
current = dateutil_tz.resolve_imaginary(current)
return self.fromdatetime(current)
def to(self, tz: TZ_EXPR) -> "Arrow":
"""Returns a new :class:`Arrow ` object, converted
to the target timezone.
:param tz: A :ref:`timezone expression `.
Usage::
>>> utc = arrow.utcnow()
>>> utc
>>> utc.to('US/Pacific')
>>> utc.to(tz.tzlocal())
>>> utc.to('-07:00')
>>> utc.to('local')
>>> utc.to('local').to('utc')
"""
if not isinstance(tz, dt_tzinfo):
tz = parser.TzinfoParser.parse(tz)
dt = self._datetime.astimezone(tz)
return self.__class__(
dt.year,
dt.month,
dt.day,
dt.hour,
dt.minute,
dt.second,
dt.microsecond,
dt.tzinfo,
fold=getattr(dt, "fold", 0),
)
# string output and formatting
def format(
self, fmt: str = "YYYY-MM-DD HH:mm:ssZZ", locale: str = DEFAULT_LOCALE
) -> str:
"""Returns a string representation of the :class:`Arrow ` object,
formatted according to the provided format string.
:param fmt: the format string.
:param locale: the locale to format.
Usage::
>>> arrow.utcnow().format('YYYY-MM-DD HH:mm:ss ZZ')
'2013-05-09 03:56:47 -00:00'
>>> arrow.utcnow().format('X')
'1368071882'
>>> arrow.utcnow().format('MMMM DD, YYYY')
'May 09, 2013'
>>> arrow.utcnow().format()
'2013-05-09 03:56:47 -00:00'
"""
return formatter.DateTimeFormatter(locale).format(self._datetime, fmt)
def humanize(
self,
other: Union["Arrow", dt_datetime, None] = None,
locale: str = DEFAULT_LOCALE,
only_distance: bool = False,
granularity: Union[_GRANULARITY, List[_GRANULARITY]] = "auto",
) -> str:
"""Returns a localized, humanized representation of a relative difference in time.
:param other: (optional) an :class:`Arrow ` or ``datetime`` object.
Defaults to now in the current :class:`Arrow ` object's timezone.
:param locale: (optional) a ``str`` specifying a locale. Defaults to 'en-us'.
:param only_distance: (optional) returns only time difference eg: "11 seconds" without "in" or "ago" part.
:param granularity: (optional) defines the precision of the output. Set it to strings 'second', 'minute',
'hour', 'day', 'week', 'month' or 'year' or a list of any combination of these strings
Usage::
>>> earlier = arrow.utcnow().shift(hours=-2)
>>> earlier.humanize()
'2 hours ago'
>>> later = earlier.shift(hours=4)
>>> later.humanize(earlier)
'in 4 hours'
"""
locale_name = locale
locale = locales.get_locale(locale)
if other is None:
utc = dt_datetime.utcnow().replace(tzinfo=dateutil_tz.tzutc())
dt = utc.astimezone(self._datetime.tzinfo)
elif isinstance(other, Arrow):
dt = other._datetime
elif isinstance(other, dt_datetime):
if other.tzinfo is None:
dt = other.replace(tzinfo=self._datetime.tzinfo)
else:
dt = other.astimezone(self._datetime.tzinfo)
else:
raise TypeError(
f"Invalid 'other' argument of type {type(other).__name__!r}. "
"Argument must be of type None, Arrow, or datetime."
)
if isinstance(granularity, list) and len(granularity) == 1:
granularity = granularity[0]
_delta = int(round((self._datetime - dt).total_seconds()))
sign = -1 if _delta < 0 else 1
delta_second = diff = abs(_delta)
try:
if granularity == "auto":
if diff < 10:
return locale.describe("now", only_distance=only_distance)
if diff < self._SECS_PER_MINUTE:
seconds = sign * delta_second
return locale.describe(
"seconds", seconds, only_distance=only_distance
)
elif diff < self._SECS_PER_MINUTE * 2:
return locale.describe("minute", sign, only_distance=only_distance)
elif diff < self._SECS_PER_HOUR:
minutes = sign * max(delta_second // self._SECS_PER_MINUTE, 2)
return locale.describe(
"minutes", minutes, only_distance=only_distance
)
elif diff < self._SECS_PER_HOUR * 2:
return locale.describe("hour", sign, only_distance=only_distance)
elif diff < self._SECS_PER_DAY:
hours = sign * max(delta_second // self._SECS_PER_HOUR, 2)
return locale.describe("hours", hours, only_distance=only_distance)
elif diff < self._SECS_PER_DAY * 2:
return locale.describe("day", sign, only_distance=only_distance)
elif diff < self._SECS_PER_WEEK:
days = sign * max(delta_second // self._SECS_PER_DAY, 2)
return locale.describe("days", days, only_distance=only_distance)
elif diff < self._SECS_PER_WEEK * 2:
return locale.describe("week", sign, only_distance=only_distance)
elif diff < self._SECS_PER_MONTH:
weeks = sign * max(delta_second // self._SECS_PER_WEEK, 2)
return locale.describe("weeks", weeks, only_distance=only_distance)
elif diff < self._SECS_PER_MONTH * 2:
return locale.describe("month", sign, only_distance=only_distance)
elif diff < self._SECS_PER_YEAR:
# TODO revisit for humanization during leap years
self_months = self._datetime.year * 12 + self._datetime.month
other_months = dt.year * 12 + dt.month
months = sign * max(abs(other_months - self_months), 2)
return locale.describe(
"months", months, only_distance=only_distance
)
elif diff < self._SECS_PER_YEAR * 2:
return locale.describe("year", sign, only_distance=only_distance)
else:
years = sign * max(delta_second // self._SECS_PER_YEAR, 2)
return locale.describe("years", years, only_distance=only_distance)
elif isinstance(granularity, str):
granularity = cast(TimeFrameLiteral, granularity) # type: ignore[assignment]
if granularity == "second":
delta = sign * float(delta_second)
if abs(delta) < 2:
return locale.describe("now", only_distance=only_distance)
elif granularity == "minute":
delta = sign * delta_second / self._SECS_PER_MINUTE
elif granularity == "hour":
delta = sign * delta_second / self._SECS_PER_HOUR
elif granularity == "day":
delta = sign * delta_second / self._SECS_PER_DAY
elif granularity == "week":
delta = sign * delta_second / self._SECS_PER_WEEK
elif granularity == "month":
delta = sign * delta_second / self._SECS_PER_MONTH
elif granularity == "quarter":
delta = sign * delta_second / self._SECS_PER_QUARTER
elif granularity == "year":
delta = sign * delta_second / self._SECS_PER_YEAR
else:
raise ValueError(
"Invalid level of granularity. "
"Please select between 'second', 'minute', 'hour', 'day', 'week', 'month', 'quarter' or 'year'."
)
if trunc(abs(delta)) != 1:
granularity += "s" # type: ignore
return locale.describe(granularity, delta, only_distance=only_distance)
else:
if not granularity:
raise ValueError(
"Empty granularity list provided. "
"Please select one or more from 'second', 'minute', 'hour', 'day', 'week', 'month', 'quarter', 'year'."
)
timeframes: List[Tuple[TimeFrameLiteral, float]] = []
def gather_timeframes(_delta: float, _frame: TimeFrameLiteral) -> float:
if _frame in granularity:
value = sign * _delta / self._SECS_MAP[_frame]
_delta %= self._SECS_MAP[_frame]
if trunc(abs(value)) != 1:
timeframes.append(
(cast(TimeFrameLiteral, _frame + "s"), value)
)
else:
timeframes.append((_frame, value))
return _delta
delta = float(delta_second)
frames: Tuple[TimeFrameLiteral, ...] = (
"year",
"quarter",
"month",
"week",
"day",
"hour",
"minute",
"second",
)
for frame in frames:
delta = gather_timeframes(delta, frame)
if len(timeframes) < len(granularity):
raise ValueError(
"Invalid level of granularity. "
"Please select between 'second', 'minute', 'hour', 'day', 'week', 'month', 'quarter' or 'year'."
)
return locale.describe_multi(timeframes, only_distance=only_distance)
except KeyError as e:
raise ValueError(
f"Humanization of the {e} granularity is not currently translated in the {locale_name!r} locale. "
"Please consider making a contribution to this locale."
)
def dehumanize(self, input_string: str, locale: str = "en_us") -> "Arrow":
"""Returns a new :class:`Arrow ` object, that represents
the time difference relative to the attrbiutes of the
:class:`Arrow ` object.
:param timestring: a ``str`` representing a humanized relative time.
:param locale: (optional) a ``str`` specifying a locale. Defaults to 'en-us'.
Usage::
>>> arw = arrow.utcnow()
>>> arw
>>> earlier = arw.dehumanize("2 days ago")
>>> earlier
>>> arw = arrow.utcnow()
>>> arw
>>> later = arw.dehumanize("in a month")
>>> later
"""
# Create a locale object based off given local
locale_obj = locales.get_locale(locale)
# Check to see if locale is supported
normalized_locale_name = locale.lower().replace("_", "-")
if normalized_locale_name not in DEHUMANIZE_LOCALES:
raise ValueError(
f"Dehumanize does not currently support the {locale} locale, please consider making a contribution to add support for this locale."
)
current_time = self.fromdatetime(self._datetime)
# Create an object containing the relative time info
time_object_info = dict.fromkeys(
["seconds", "minutes", "hours", "days", "weeks", "months", "years"], 0
)
# Create an object representing if unit has been seen
unit_visited = dict.fromkeys(
["now", "seconds", "minutes", "hours", "days", "weeks", "months", "years"],
False,
)
# Create a regex pattern object for numbers
num_pattern = re.compile(r"\d+")
# Search input string for each time unit within locale
for unit, unit_object in locale_obj.timeframes.items():
# Need to check the type of unit_object to create the correct dictionary
if isinstance(unit_object, Mapping):
strings_to_search = unit_object
else:
strings_to_search = {unit: str(unit_object)}
# Search for any matches that exist for that locale's unit.
# Needs to cycle all through strings as some locales have strings that
# could overlap in a regex match, since input validation isn't being performed.
for time_delta, time_string in strings_to_search.items():
# Replace {0} with regex \d representing digits
search_string = str(time_string)
search_string = search_string.format(r"\d+")
# Create search pattern and find within string
pattern = re.compile(fr"{search_string}")
match = pattern.search(input_string)
# If there is no match continue to next iteration
if not match:
continue
match_string = match.group()
num_match = num_pattern.search(match_string)
# If no number matches
# Need for absolute value as some locales have signs included in their objects
if not num_match:
change_value = (
1 if not time_delta.isnumeric() else abs(int(time_delta))
)
else:
change_value = int(num_match.group())
# No time to update if now is the unit
if unit == "now":
unit_visited[unit] = True
continue
# Add change value to the correct unit (incorporates the plurality that exists within timeframe i.e second v.s seconds)
time_unit_to_change = str(unit)
time_unit_to_change += (
"s" if (str(time_unit_to_change)[-1] != "s") else ""
)
time_object_info[time_unit_to_change] = change_value
unit_visited[time_unit_to_change] = True
# Assert error if string does not modify any units
if not any([True for k, v in unit_visited.items() if v]):
raise ValueError(
"Input string not valid. Note: Some locales do not support the week granulairty in Arrow. "
"If you are attempting to use the week granularity on an unsupported locale, this could be the cause of this error."
)
# Sign logic
future_string = locale_obj.future
future_string = future_string.format(".*")
future_pattern = re.compile(fr"^{future_string}$")
future_pattern_match = future_pattern.findall(input_string)
past_string = locale_obj.past
past_string = past_string.format(".*")
past_pattern = re.compile(fr"^{past_string}$")
past_pattern_match = past_pattern.findall(input_string)
# If a string contains the now unit, there will be no relative units, hence the need to check if the now unit
# was visited before raising a ValueError
if past_pattern_match:
sign_val = -1
elif future_pattern_match:
sign_val = 1
elif unit_visited["now"]:
sign_val = 0
else:
raise ValueError(
"Invalid input String. String does not contain any relative time information. "
"String should either represent a time in the future or a time in the past. "
"Ex: 'in 5 seconds' or '5 seconds ago'."
)
time_changes = {k: sign_val * v for k, v in time_object_info.items()}
return current_time.shift(**time_changes)
# query functions
def is_between(
self,
start: "Arrow",
end: "Arrow",
bounds: _BOUNDS = "()",
) -> bool:
"""Returns a boolean denoting whether the :class:`Arrow ` object is between
the start and end limits.
:param start: an :class:`Arrow ` object.
:param end: an :class:`Arrow ` object.
:param bounds: (optional) a ``str`` of either '()', '(]', '[)', or '[]' that specifies
whether to include or exclude the start and end values in the range. '(' excludes
the start, '[' includes the start, ')' excludes the end, and ']' includes the end.
If the bounds are not specified, the default bound '()' is used.
Usage::
>>> start = arrow.get(datetime(2013, 5, 5, 12, 30, 10))
>>> end = arrow.get(datetime(2013, 5, 5, 12, 30, 36))
>>> arrow.get(datetime(2013, 5, 5, 12, 30, 27)).is_between(start, end)
True
>>> start = arrow.get(datetime(2013, 5, 5))
>>> end = arrow.get(datetime(2013, 5, 8))
>>> arrow.get(datetime(2013, 5, 8)).is_between(start, end, '[]')
True
>>> start = arrow.get(datetime(2013, 5, 5))
>>> end = arrow.get(datetime(2013, 5, 8))
>>> arrow.get(datetime(2013, 5, 8)).is_between(start, end, '[)')
False
"""
util.validate_bounds(bounds)
if not isinstance(start, Arrow):
raise TypeError(
f"Cannot parse start date argument type of {type(start)!r}."
)
if not isinstance(end, Arrow):
raise TypeError(f"Cannot parse end date argument type of {type(start)!r}.")
include_start = bounds[0] == "["
include_end = bounds[1] == "]"
target_ts = self.float_timestamp
start_ts = start.float_timestamp
end_ts = end.float_timestamp
return (
(start_ts <= target_ts <= end_ts)
and (include_start or start_ts < target_ts)
and (include_end or target_ts < end_ts)
)
# datetime methods
def date(self) -> date:
"""Returns a ``date`` object with the same year, month and day.
Usage::
>>> arrow.utcnow().date()
datetime.date(2019, 1, 23)
"""
return self._datetime.date()
def time(self) -> dt_time:
"""Returns a ``time`` object with the same hour, minute, second, microsecond.
Usage::
>>> arrow.utcnow().time()
datetime.time(12, 15, 34, 68352)
"""
return self._datetime.time()
def timetz(self) -> dt_time:
"""Returns a ``time`` object with the same hour, minute, second, microsecond and
tzinfo.
Usage::
>>> arrow.utcnow().timetz()
datetime.time(12, 5, 18, 298893, tzinfo=tzutc())
"""
return self._datetime.timetz()
def astimezone(self, tz: Optional[dt_tzinfo]) -> dt_datetime:
"""Returns a ``datetime`` object, converted to the specified timezone.
:param tz: a ``tzinfo`` object.
Usage::
>>> pacific=arrow.now('US/Pacific')
>>> nyc=arrow.now('America/New_York').tzinfo
>>> pacific.astimezone(nyc)
datetime.datetime(2019, 1, 20, 10, 24, 22, 328172, tzinfo=tzfile('/usr/share/zoneinfo/America/New_York'))
"""
return self._datetime.astimezone(tz)
def utcoffset(self) -> Optional[timedelta]:
"""Returns a ``timedelta`` object representing the whole number of minutes difference from
UTC time.
Usage::
>>> arrow.now('US/Pacific').utcoffset()
datetime.timedelta(-1, 57600)
"""
return self._datetime.utcoffset()
def dst(self) -> Optional[timedelta]:
"""Returns the daylight savings time adjustment.
Usage::
>>> arrow.utcnow().dst()
datetime.timedelta(0)
"""
return self._datetime.dst()
def timetuple(self) -> struct_time:
"""Returns a ``time.struct_time``, in the current timezone.
Usage::
>>> arrow.utcnow().timetuple()
time.struct_time(tm_year=2019, tm_mon=1, tm_mday=20, tm_hour=15, tm_min=17, tm_sec=8, tm_wday=6, tm_yday=20, tm_isdst=0)
"""
return self._datetime.timetuple()
def utctimetuple(self) -> struct_time:
"""Returns a ``time.struct_time``, in UTC time.
Usage::
>>> arrow.utcnow().utctimetuple()
time.struct_time(tm_year=2019, tm_mon=1, tm_mday=19, tm_hour=21, tm_min=41, tm_sec=7, tm_wday=5, tm_yday=19, tm_isdst=0)
"""
return self._datetime.utctimetuple()
def toordinal(self) -> int:
"""Returns the proleptic Gregorian ordinal of the date.
Usage::
>>> arrow.utcnow().toordinal()
737078
"""
return self._datetime.toordinal()
def weekday(self) -> int:
"""Returns the day of the week as an integer (0-6).
Usage::
>>> arrow.utcnow().weekday()
5
"""
return self._datetime.weekday()
def isoweekday(self) -> int:
"""Returns the ISO day of the week as an integer (1-7).
Usage::
>>> arrow.utcnow().isoweekday()
6
"""
return self._datetime.isoweekday()
def isocalendar(self) -> Tuple[int, int, int]:
"""Returns a 3-tuple, (ISO year, ISO week number, ISO weekday).
Usage::
>>> arrow.utcnow().isocalendar()
(2019, 3, 6)
"""
return self._datetime.isocalendar()
def isoformat(self, sep: str = "T", timespec: str = "auto") -> str:
"""Returns an ISO 8601 formatted representation of the date and time.
Usage::
>>> arrow.utcnow().isoformat()
'2019-01-19T18:30:52.442118+00:00'
"""
return self._datetime.isoformat(sep, timespec)
def ctime(self) -> str:
"""Returns a ctime formatted representation of the date and time.
Usage::
>>> arrow.utcnow().ctime()
'Sat Jan 19 18:26:50 2019'
"""
return self._datetime.ctime()
def strftime(self, format: str) -> str:
"""Formats in the style of ``datetime.strftime``.
:param format: the format string.
Usage::
>>> arrow.utcnow().strftime('%d-%m-%Y %H:%M:%S')
'23-01-2019 12:28:17'
"""
return self._datetime.strftime(format)
def for_json(self) -> str:
"""Serializes for the ``for_json`` protocol of simplejson.
Usage::
>>> arrow.utcnow().for_json()
'2019-01-19T18:25:36.760079+00:00'
"""
return self.isoformat()
# math
def __add__(self, other: Any) -> "Arrow":
if isinstance(other, (timedelta, relativedelta)):
return self.fromdatetime(self._datetime + other, self._datetime.tzinfo)
return NotImplemented
def __radd__(self, other: Union[timedelta, relativedelta]) -> "Arrow":
return self.__add__(other)
@overload
def __sub__(self, other: Union[timedelta, relativedelta]) -> "Arrow":
pass # pragma: no cover
@overload
def __sub__(self, other: Union[dt_datetime, "Arrow"]) -> timedelta:
pass # pragma: no cover
def __sub__(self, other: Any) -> Union[timedelta, "Arrow"]:
if isinstance(other, (timedelta, relativedelta)):
return self.fromdatetime(self._datetime - other, self._datetime.tzinfo)
elif isinstance(other, dt_datetime):
return self._datetime - other
elif isinstance(other, Arrow):
return self._datetime - other._datetime
return NotImplemented
def __rsub__(self, other: Any) -> timedelta:
if isinstance(other, dt_datetime):
return other - self._datetime
return NotImplemented
# comparisons
def __eq__(self, other: Any) -> bool:
if not isinstance(other, (Arrow, dt_datetime)):
return False
return self._datetime == self._get_datetime(other)
def __ne__(self, other: Any) -> bool:
if not isinstance(other, (Arrow, dt_datetime)):
return True
return not self.__eq__(other)
def __gt__(self, other: Any) -> bool:
if not isinstance(other, (Arrow, dt_datetime)):
return NotImplemented
return self._datetime > self._get_datetime(other)
def __ge__(self, other: Any) -> bool:
if not isinstance(other, (Arrow, dt_datetime)):
return NotImplemented
return self._datetime >= self._get_datetime(other)
def __lt__(self, other: Any) -> bool:
if not isinstance(other, (Arrow, dt_datetime)):
return NotImplemented
return self._datetime < self._get_datetime(other)
def __le__(self, other: Any) -> bool:
if not isinstance(other, (Arrow, dt_datetime)):
return NotImplemented
return self._datetime <= self._get_datetime(other)
# internal methods
@staticmethod
def _get_tzinfo(tz_expr: Optional[TZ_EXPR]) -> dt_tzinfo:
"""Get normalized tzinfo object from various inputs."""
if tz_expr is None:
return dateutil_tz.tzutc()
if isinstance(tz_expr, dt_tzinfo):
return tz_expr
else:
try:
return parser.TzinfoParser.parse(tz_expr)
except parser.ParserError:
raise ValueError(f"{tz_expr!r} not recognized as a timezone.")
@classmethod
def _get_datetime(
cls, expr: Union["Arrow", dt_datetime, int, float, str]
) -> dt_datetime:
"""Get datetime object from a specified expression."""
if isinstance(expr, Arrow):
return expr.datetime
elif isinstance(expr, dt_datetime):
return expr
elif util.is_timestamp(expr):
timestamp = float(expr)
return cls.utcfromtimestamp(timestamp).datetime
else:
raise ValueError(f"{expr!r} not recognized as a datetime or timestamp.")
@classmethod
def _get_frames(cls, name: _T_FRAMES) -> Tuple[str, str, int]:
"""Finds relevant timeframe and steps for use in range and span methods.
Returns a 3 element tuple in the form (frame, plural frame, step), for example ("day", "days", 1)
"""
if name in cls._ATTRS:
return name, f"{name}s", 1
elif name[-1] == "s" and name[:-1] in cls._ATTRS:
return name[:-1], name, 1
elif name in ["week", "weeks"]:
return "week", "weeks", 1
elif name in ["quarter", "quarters"]:
return "quarter", "months", 3
else:
supported = ", ".join(
[
"year(s)",
"month(s)",
"day(s)",
"hour(s)",
"minute(s)",
"second(s)",
"microsecond(s)",
"week(s)",
"quarter(s)",
]
)
raise ValueError(
f"Range or span over frame {name} not supported. Supported frames: {supported}."
)
@classmethod
def _get_iteration_params(cls, end: Any, limit: Optional[int]) -> Tuple[Any, int]:
"""Sets default end and limit values for range method."""
if end is None:
if limit is None:
raise ValueError("One of 'end' or 'limit' is required.")
return cls.max, limit
else:
if limit is None:
return end, sys.maxsize
return end, limit
@staticmethod
def _is_last_day_of_month(date: "Arrow") -> bool:
"""Returns a boolean indicating whether the datetime is the last day of the month."""
return date.day == calendar.monthrange(date.year, date.month)[1]
Arrow.min = Arrow.fromdatetime(dt_datetime.min)
Arrow.max = Arrow.fromdatetime(dt_datetime.max)
python-arrow-1.2.1/arrow/constants.py 0000664 0000000 0000000 00000005530 14151001236 0017700 0 ustar 00root root 0000000 0000000 """Constants used internally in arrow."""
import sys
from datetime import datetime
if sys.version_info < (3, 8): # pragma: no cover
from typing_extensions import Final
else:
from typing import Final # pragma: no cover
# datetime.max.timestamp() errors on Windows, so we must hardcode
# the highest possible datetime value that can output a timestamp.
# tl;dr platform-independent max timestamps are hard to form
# See: https://stackoverflow.com/q/46133223
try:
# Get max timestamp. Works on POSIX-based systems like Linux and macOS,
# but will trigger an OverflowError, ValueError, or OSError on Windows
_MAX_TIMESTAMP = datetime.max.timestamp()
except (OverflowError, ValueError, OSError): # pragma: no cover
# Fallback for Windows and 32-bit systems if initial max timestamp call fails
# Must get max value of ctime on Windows based on architecture (x32 vs x64)
# https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/ctime-ctime32-ctime64-wctime-wctime32-wctime64
# Note: this may occur on both 32-bit Linux systems (issue #930) along with Windows systems
is_64bits = sys.maxsize > 2 ** 32
_MAX_TIMESTAMP = (
datetime(3000, 1, 1, 23, 59, 59, 999999).timestamp()
if is_64bits
else datetime(2038, 1, 1, 23, 59, 59, 999999).timestamp()
)
MAX_TIMESTAMP: Final[float] = _MAX_TIMESTAMP
MAX_TIMESTAMP_MS: Final[float] = MAX_TIMESTAMP * 1000
MAX_TIMESTAMP_US: Final[float] = MAX_TIMESTAMP * 1_000_000
MAX_ORDINAL: Final[int] = datetime.max.toordinal()
MIN_ORDINAL: Final[int] = 1
DEFAULT_LOCALE: Final[str] = "en-us"
# Supported dehumanize locales
DEHUMANIZE_LOCALES = {
"en",
"en-us",
"en-gb",
"en-au",
"en-be",
"en-jp",
"en-za",
"en-ca",
"en-ph",
"fr",
"fr-fr",
"fr-ca",
"it",
"it-it",
"es",
"es-es",
"el",
"el-gr",
"ja",
"ja-jp",
"se",
"se-fi",
"se-no",
"se-se",
"sv",
"sv-se",
"fi",
"fi-fi",
"zh",
"zh-cn",
"zh-tw",
"zh-hk",
"nl",
"nl-nl",
"af",
"de",
"de-de",
"de-ch",
"de-at",
"nb",
"nb-no",
"nn",
"nn-no",
"pt",
"pt-pt",
"pt-br",
"tl",
"tl-ph",
"vi",
"vi-vn",
"tr",
"tr-tr",
"az",
"az-az",
"da",
"da-dk",
"ml",
"hi",
"fa",
"fa-ir",
"mr",
"ca",
"ca-es",
"ca-ad",
"ca-fr",
"ca-it",
"eo",
"eo-xx",
"bn",
"bn-bd",
"bn-in",
"rm",
"rm-ch",
"ro",
"ro-ro",
"sl",
"sl-si",
"id",
"id-id",
"ne",
"ne-np",
"ee",
"et",
"sw",
"sw-ke",
"sw-tz",
"la",
"la-va",
"lt",
"lt-lt",
"ms",
"ms-my",
"ms-bn",
"or",
"or-in",
"lb",
"lb-lu",
"zu",
"zu-za",
"sq",
"sq-al",
"ta",
"ta-in",
"ta-lk",
"ur",
"ur-pk",
}
python-arrow-1.2.1/arrow/factory.py 0000664 0000000 0000000 00000026253 14151001236 0017340 0 ustar 00root root 0000000 0000000 """
Implements the :class:`ArrowFactory ` class,
providing factory methods for common :class:`Arrow `
construction scenarios.
"""
import calendar
from datetime import date, datetime
from datetime import tzinfo as dt_tzinfo
from decimal import Decimal
from time import struct_time
from typing import Any, List, Optional, Tuple, Type, Union, overload
from dateutil import tz as dateutil_tz
from arrow import parser
from arrow.arrow import TZ_EXPR, Arrow
from arrow.constants import DEFAULT_LOCALE
from arrow.util import is_timestamp, iso_to_gregorian
class ArrowFactory:
"""A factory for generating :class:`Arrow ` objects.
:param type: (optional) the :class:`Arrow `-based class to construct from.
Defaults to :class:`Arrow `.
"""
type: Type[Arrow]
def __init__(self, type: Type[Arrow] = Arrow) -> None:
self.type = type
@overload
def get(
self,
*,
locale: str = DEFAULT_LOCALE,
tzinfo: Optional[TZ_EXPR] = None,
normalize_whitespace: bool = False,
) -> Arrow:
... # pragma: no cover
@overload
def get(
self,
__obj: Union[
Arrow,
datetime,
date,
struct_time,
dt_tzinfo,
int,
float,
str,
Tuple[int, int, int],
],
*,
locale: str = DEFAULT_LOCALE,
tzinfo: Optional[TZ_EXPR] = None,
normalize_whitespace: bool = False,
) -> Arrow:
... # pragma: no cover
@overload
def get(
self,
__arg1: Union[datetime, date],
__arg2: TZ_EXPR,
*,
locale: str = DEFAULT_LOCALE,
tzinfo: Optional[TZ_EXPR] = None,
normalize_whitespace: bool = False,
) -> Arrow:
... # pragma: no cover
@overload
def get(
self,
__arg1: str,
__arg2: Union[str, List[str]],
*,
locale: str = DEFAULT_LOCALE,
tzinfo: Optional[TZ_EXPR] = None,
normalize_whitespace: bool = False,
) -> Arrow:
... # pragma: no cover
def get(self, *args: Any, **kwargs: Any) -> Arrow:
"""Returns an :class:`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 ` 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()
**One** :class:`Arrow ` object, to get a copy.
>>> arw = arrow.utcnow()
>>> arrow.get(arw)
**One** ``float`` or ``int``, convertible to a floating-point timestamp, to get
that timestamp in UTC::
>>> arrow.get(1367992474.293378)
>>> arrow.get(1367992474)
**One** ISO 8601-formatted ``str``, to parse it::
>>> arrow.get('2013-09-29T01:26:43.830580')
**One** ISO 8601-formatted ``str``, in basic format, to parse it::
>>> arrow.get('20160413T133656.456289')
**One** ``tzinfo``, to get the current time **converted** to that timezone::
>>> arrow.get(tz.tzlocal())
**One** naive ``datetime``, to get that datetime in UTC::
>>> arrow.get(datetime(2013, 5, 5))
**One** aware ``datetime``, to get that datetime::
>>> arrow.get(datetime(2013, 5, 5, tzinfo=tz.tzlocal()))
**One** naive ``date``, to get that date in UTC::
>>> arrow.get(date(2013, 5, 5))
**One** time.struct time::
>>> arrow.get(gmtime(0))
**One** iso calendar ``tuple``, to get that week date in UTC::
>>> arrow.get((2013, 18, 7))
**Two** arguments, a naive or aware ``datetime``, and a replacement
:ref:`timezone expression `::
>>> arrow.get(datetime(2013, 5, 5), 'US/Pacific')
**Two** arguments, a naive ``date``, and a replacement
:ref:`timezone expression `::
>>> arrow.get(date(2013, 5, 5), 'US/Pacific')
**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')
**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'])
**Three or more** arguments, as for the direct constructor of an ``Arrow`` object::
>>> arrow.get(2013, 5, 5, 12, 30, 45)
"""
arg_count = len(args)
locale = kwargs.pop("locale", DEFAULT_LOCALE)
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, @ tzinfo or utc
if arg_count == 0:
if isinstance(tz, str):
tz = parser.TzinfoParser.parse(tz)
return self.type.now(tzinfo=tz)
if isinstance(tz, dt_tzinfo):
return self.type.now(tzinfo=tz)
return self.type.utcnow()
if arg_count == 1:
arg = args[0]
if isinstance(arg, Decimal):
arg = float(arg)
# (None) -> raises an exception
if arg is None:
raise TypeError("Cannot parse argument of type None.")
# try (int, float) -> from timestamp @ tzinfo
elif not isinstance(arg, str) 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 @ tzinfo
elif isinstance(arg, Arrow):
return self.type.fromdatetime(arg.datetime, tzinfo=tz)
# (datetime) -> from datetime @ tzinfo
elif isinstance(arg, datetime):
return self.type.fromdatetime(arg, tzinfo=tz)
# (date) -> from date @ tzinfo
elif isinstance(arg, date):
return self.type.fromdate(arg, tzinfo=tz)
# (tzinfo) -> now @ tzinfo
elif isinstance(arg, dt_tzinfo):
return self.type.now(tzinfo=arg)
# (str) -> parse @ tzinfo
elif isinstance(arg, str):
dt = parser.DateTimeParser(locale).parse_iso(arg, normalize_whitespace)
return self.type.fromdatetime(dt, tzinfo=tz)
# (struct_time) -> from struct_time
elif isinstance(arg, struct_time):
return self.type.utcfromtimestamp(calendar.timegm(arg))
# (iso calendar) -> convert then from date @ tzinfo
elif isinstance(arg, tuple) and len(arg) == 3:
d = iso_to_gregorian(*arg)
return self.type.fromdate(d, tzinfo=tz)
else:
raise TypeError(f"Cannot parse single argument of type {type(arg)!r}.")
elif arg_count == 2:
arg_1, arg_2 = args[0], args[1]
if isinstance(arg_1, datetime):
# (datetime, tzinfo/str) -> fromdatetime @ tzinfo
if isinstance(arg_2, (dt_tzinfo, str)):
return self.type.fromdatetime(arg_1, tzinfo=arg_2)
else:
raise TypeError(
f"Cannot parse two arguments of types 'datetime', {type(arg_2)!r}."
)
elif isinstance(arg_1, date):
# (date, tzinfo/str) -> fromdate @ tzinfo
if isinstance(arg_2, (dt_tzinfo, str)):
return self.type.fromdate(arg_1, tzinfo=arg_2)
else:
raise TypeError(
f"Cannot parse two arguments of types 'date', {type(arg_2)!r}."
)
# (str, format) -> parse @ tzinfo
elif isinstance(arg_1, str) and isinstance(arg_2, (str, list)):
dt = parser.DateTimeParser(locale).parse(
args[0], args[1], normalize_whitespace
)
return self.type.fromdatetime(dt, tzinfo=tz)
else:
raise TypeError(
f"Cannot parse two arguments of types {type(arg_1)!r} and {type(arg_2)!r}."
)
# 3+ args -> datetime-like via constructor
else:
return self.type(*args, **kwargs)
def utcnow(self) -> Arrow:
"""Returns an :class:`Arrow ` object, representing "now" in UTC time.
Usage::
>>> import arrow
>>> arrow.utcnow()
"""
return self.type.utcnow()
def now(self, tz: Optional[TZ_EXPR] = None) -> Arrow:
"""Returns an :class:`Arrow ` object, representing "now" in the given
timezone.
:param tz: (optional) A :ref:`timezone expression `. Defaults to local time.
Usage::
>>> import arrow
>>> arrow.now()
>>> arrow.now('US/Pacific')
>>> arrow.now('+02:00')
>>> arrow.now('local')
"""
if tz is None:
tz = dateutil_tz.tzlocal()
elif not isinstance(tz, dt_tzinfo):
tz = parser.TzinfoParser.parse(tz)
return self.type.now(tz)
python-arrow-1.2.1/arrow/formatter.py 0000664 0000000 0000000 00000012227 14151001236 0017670 0 ustar 00root root 0000000 0000000 """Provides the :class:`Arrow ` class, an improved formatter for datetimes."""
import re
import sys
from datetime import datetime, timedelta
from typing import Optional, Pattern, cast
from dateutil import tz as dateutil_tz
from arrow import locales
from arrow.constants import DEFAULT_LOCALE
if sys.version_info < (3, 8): # pragma: no cover
from typing_extensions import Final
else:
from typing import Final # pragma: no cover
FORMAT_ATOM: Final[str] = "YYYY-MM-DD HH:mm:ssZZ"
FORMAT_COOKIE: Final[str] = "dddd, DD-MMM-YYYY HH:mm:ss ZZZ"
FORMAT_RFC822: Final[str] = "ddd, DD MMM YY HH:mm:ss Z"
FORMAT_RFC850: Final[str] = "dddd, DD-MMM-YY HH:mm:ss ZZZ"
FORMAT_RFC1036: Final[str] = "ddd, DD MMM YY HH:mm:ss Z"
FORMAT_RFC1123: Final[str] = "ddd, DD MMM YYYY HH:mm:ss Z"
FORMAT_RFC2822: Final[str] = "ddd, DD MMM YYYY HH:mm:ss Z"
FORMAT_RFC3339: Final[str] = "YYYY-MM-DD HH:mm:ssZZ"
FORMAT_RSS: Final[str] = "ddd, DD MMM YYYY HH:mm:ss Z"
FORMAT_W3C: Final[str] = "YYYY-MM-DD HH:mm:ssZZ"
class DateTimeFormatter:
# 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: Final[Pattern[str]] = re.compile(
r"(\[(?:(?=(?P[^]]))(?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)"
)
locale: locales.Locale
def __init__(self, locale: str = DEFAULT_LOCALE) -> None:
self.locale = locales.get_locale(locale)
def format(cls, dt: datetime, fmt: str) -> str:
# FIXME: _format_token() is nullable
return cls._FORMAT_RE.sub(
lambda m: cast(str, cls._format_token(dt, m.group(0))), fmt
)
def _format_token(self, dt: datetime, token: Optional[str]) -> Optional[str]:
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 f"{dt.month:02d}"
if token == "M":
return f"{dt.month}"
if token == "DDDD":
return f"{dt.timetuple().tm_yday:03d}"
if token == "DDD":
return f"{dt.timetuple().tm_yday}"
if token == "DD":
return f"{dt.day:02d}"
if token == "D":
return f"{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 f"{dt.isoweekday()}"
if token == "HH":
return f"{dt.hour:02d}"
if token == "H":
return f"{dt.hour}"
if token == "hh":
return f"{dt.hour if 0 < dt.hour < 13 else abs(dt.hour - 12):02d}"
if token == "h":
return f"{dt.hour if 0 < dt.hour < 13 else abs(dt.hour - 12)}"
if token == "mm":
return f"{dt.minute:02d}"
if token == "m":
return f"{dt.minute}"
if token == "ss":
return f"{dt.second:02d}"
if token == "s":
return f"{dt.second}"
if token == "SSSSSS":
return f"{dt.microsecond:06d}"
if token == "SSSSS":
return f"{dt.microsecond // 10:05d}"
if token == "SSSS":
return f"{dt.microsecond // 100:04d}"
if token == "SSS":
return f"{dt.microsecond // 1000:03d}"
if token == "SS":
return f"{dt.microsecond // 10000:02d}"
if token == "S":
return f"{dt.microsecond // 100000}"
if token == "X":
return f"{dt.timestamp()}"
if token == "x":
return f"{dt.timestamp() * 1_000_000:.0f}"
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
# `dt` must be aware object. Otherwise, this line will raise AttributeError
# https://github.com/arrow-py/arrow/pull/883#discussion_r529866834
# datetime awareness: https://docs.python.org/3/library/datetime.html#aware-and-naive-objects
total_minutes = int(cast(timedelta, tz.utcoffset(dt)).total_seconds() / 60)
sign = "+" if total_minutes >= 0 else "-"
total_minutes = abs(total_minutes)
hour, minute = divmod(total_minutes, 60)
return f"{sign}{hour:02d}{separator}{minute:02d}"
if token in ("a", "A"):
return self.locale.meridian(dt.hour, token)
if token == "W":
year, week, day = dt.isocalendar()
return f"{year}-W{week:02d}-{day}"
python-arrow-1.2.1/arrow/locales.py 0000664 0000000 0000000 00000417635 14151001236 0017323 0 ustar 00root root 0000000 0000000 """Provides internationalization for arrow in over 60 languages and dialects."""
import sys
from math import trunc
from typing import (
Any,
ClassVar,
Dict,
List,
Mapping,
Optional,
Sequence,
Tuple,
Type,
Union,
cast,
)
if sys.version_info < (3, 8): # pragma: no cover
from typing_extensions import Literal
else:
from typing import Literal # pragma: no cover
TimeFrameLiteral = Literal[
"now",
"second",
"seconds",
"minute",
"minutes",
"hour",
"hours",
"day",
"days",
"week",
"weeks",
"month",
"months",
"quarter",
"quarters",
"year",
"years",
]
_TimeFrameElements = Union[
str, Sequence[str], Mapping[str, str], Mapping[str, Sequence[str]]
]
_locale_map: Dict[str, Type["Locale"]] = dict()
def get_locale(name: str) -> "Locale":
"""Returns an appropriate :class:`Locale `
corresponding to an input locale name.
:param name: the name of the locale.
"""
normalized_locale_name = name.lower().replace("_", "-")
locale_cls = _locale_map.get(normalized_locale_name)
if locale_cls is None:
raise ValueError(f"Unsupported locale {normalized_locale_name!r}.")
return locale_cls()
def get_locale_by_class_name(name: str) -> "Locale":
"""Returns an appropriate :class:`Locale `
corresponding to an locale class name.
:param name: the name of the locale class.
"""
locale_cls: Optional[Type[Locale]] = globals().get(name)
if locale_cls is None:
raise ValueError(f"Unsupported locale {name!r}.")
return locale_cls()
class Locale:
"""Represents locale-specific data and functionality."""
names: ClassVar[List[str]] = []
timeframes: ClassVar[Mapping[TimeFrameLiteral, _TimeFrameElements]] = {
"now": "",
"second": "",
"seconds": "",
"minute": "",
"minutes": "",
"hour": "",
"hours": "",
"day": "",
"days": "",
"week": "",
"weeks": "",
"month": "",
"months": "",
"quarter": "",
"quarters": "",
"year": "",
"years": "",
}
meridians: ClassVar[Dict[str, str]] = {"am": "", "pm": "", "AM": "", "PM": ""}
past: ClassVar[str]
future: ClassVar[str]
and_word: ClassVar[Optional[str]] = None
month_names: ClassVar[List[str]] = []
month_abbreviations: ClassVar[List[str]] = []
day_names: ClassVar[List[str]] = []
day_abbreviations: ClassVar[List[str]] = []
ordinal_day_re: ClassVar[str] = r"(\d+)"
_month_name_to_ordinal: Optional[Dict[str, int]]
def __init_subclass__(cls, **kwargs: Any) -> None:
for locale_name in cls.names:
if locale_name in _locale_map:
raise LookupError(f"Duplicated locale name: {locale_name}")
_locale_map[locale_name.lower().replace("_", "-")] = cls
def __init__(self) -> None:
self._month_name_to_ordinal = None
def describe(
self,
timeframe: TimeFrameLiteral,
delta: Union[float, int] = 0,
only_distance: bool = False,
) -> str:
"""Describes a delta within a timeframe in plain language.
:param timeframe: a string representing a timeframe.
:param delta: a quantity representing a delta in a timeframe.
:param only_distance: return only distance eg: "11 seconds" without "in" or "ago" keywords
"""
humanized = self._format_timeframe(timeframe, trunc(delta))
if not only_distance:
humanized = self._format_relative(humanized, timeframe, delta)
return humanized
def describe_multi(
self,
timeframes: Sequence[Tuple[TimeFrameLiteral, Union[int, float]]],
only_distance: bool = False,
) -> str:
"""Describes a delta within multiple timeframes in plain language.
:param timeframes: a list of string, quantity pairs each representing a timeframe and delta.
:param only_distance: return only distance eg: "2 hours and 11 seconds" without "in" or "ago" keywords
"""
parts = [
self._format_timeframe(timeframe, trunc(delta))
for timeframe, delta in timeframes
]
if self.and_word:
parts.insert(-1, self.and_word)
humanized = " ".join(parts)
if not only_distance:
humanized = self._format_relative(humanized, *timeframes[-1])
return humanized
def day_name(self, day: int) -> str:
"""Returns the day name for a specified day of the week.
:param day: the ``int`` day of the week (1-7).
"""
return self.day_names[day]
def day_abbreviation(self, day: int) -> str:
"""Returns the day abbreviation for a specified day of the week.
:param day: the ``int`` day of the week (1-7).
"""
return self.day_abbreviations[day]
def month_name(self, month: int) -> str:
"""Returns the month name for a specified month of the year.
:param month: the ``int`` month of the year (1-12).
"""
return self.month_names[month]
def month_abbreviation(self, month: int) -> str:
"""Returns the month abbreviation for a specified month of the year.
:param month: the ``int`` month of the year (1-12).
"""
return self.month_abbreviations[month]
def month_number(self, name: str) -> Optional[int]:
"""Returns the month number for a month specified by name or abbreviation.
:param name: the month name or abbreviation.
"""
if self._month_name_to_ordinal is None:
self._month_name_to_ordinal = self._name_to_ordinal(self.month_names)
self._month_name_to_ordinal.update(
self._name_to_ordinal(self.month_abbreviations)
)
return self._month_name_to_ordinal.get(name)
def year_full(self, year: int) -> str:
"""Returns the year for specific locale if available
:param year: the ``int`` year (4-digit)
"""
return f"{year:04d}"
def year_abbreviation(self, year: int) -> str:
"""Returns the year for specific locale if available
:param year: the ``int`` year (4-digit)
"""
return f"{year:04d}"[2:]
def meridian(self, hour: int, token: Any) -> Optional[str]:
"""Returns the meridian indicator for a specified hour and format token.
:param hour: the ``int`` hour of the day.
:param token: the format token.
"""
if token == "a":
return self.meridians["am"] if hour < 12 else self.meridians["pm"]
if token == "A":
return self.meridians["AM"] if hour < 12 else self.meridians["PM"]
return None
def ordinal_number(self, n: int) -> str:
"""Returns the ordinal format of a given integer
:param n: an integer
"""
return self._ordinal_number(n)
def _ordinal_number(self, n: int) -> str:
return f"{n}"
def _name_to_ordinal(self, lst: Sequence[str]) -> Dict[str, int]:
return {elem.lower(): i for i, elem in enumerate(lst[1:], 1)}
def _format_timeframe(self, timeframe: TimeFrameLiteral, delta: int) -> str:
# TODO: remove cast
return cast(str, self.timeframes[timeframe]).format(trunc(abs(delta)))
def _format_relative(
self,
humanized: str,
timeframe: TimeFrameLiteral,
delta: Union[float, int],
) -> str:
if timeframe == "now":
return humanized
direction = self.past if delta < 0 else self.future
return direction.format(humanized)
class EnglishLocale(Locale):
names = [
"en",
"en-us",
"en-gb",
"en-au",
"en-be",
"en-jp",
"en-za",
"en-ca",
"en-ph",
]
past = "{0} ago"
future = "in {0}"
and_word = "and"
timeframes = {
"now": "just now",
"second": "a second",
"seconds": "{0} seconds",
"minute": "a minute",
"minutes": "{0} minutes",
"hour": "an hour",
"hours": "{0} hours",
"day": "a day",
"days": "{0} days",
"week": "a week",
"weeks": "{0} weeks",
"month": "a month",
"months": "{0} months",
"quarter": "a quarter",
"quarters": "{0} quarters",
"year": "a year",
"years": "{0} years",
}
meridians = {"am": "am", "pm": "pm", "AM": "AM", "PM": "PM"}
month_names = [
"",
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December",
]
month_abbreviations = [
"",
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec",
]
day_names = [
"",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
"Sunday",
]
day_abbreviations = ["", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
ordinal_day_re = r"((?P[2-3]?1(?=st)|[2-3]?2(?=nd)|[2-3]?3(?=rd)|[1-3]?[04-9](?=th)|1[1-3](?=th))(st|nd|rd|th))"
def _ordinal_number(self, n: int) -> str:
if n % 100 not in (11, 12, 13):
remainder = abs(n) % 10
if remainder == 1:
return f"{n}st"
elif remainder == 2:
return f"{n}nd"
elif remainder == 3:
return f"{n}rd"
return f"{n}th"
def describe(
self,
timeframe: TimeFrameLiteral,
delta: Union[int, float] = 0,
only_distance: bool = False,
) -> str:
"""Describes a delta within a timeframe in plain language.
:param timeframe: a string representing a timeframe.
:param delta: a quantity representing a delta in a timeframe.
:param only_distance: return only distance eg: "11 seconds" without "in" or "ago" keywords
"""
humanized = super().describe(timeframe, delta, only_distance)
if only_distance and timeframe == "now":
humanized = "instantly"
return humanized
class ItalianLocale(Locale):
names = ["it", "it-it"]
past = "{0} fa"
future = "tra {0}"
and_word = "e"
timeframes = {
"now": "adesso",
"second": "un secondo",
"seconds": "{0} qualche secondo",
"minute": "un minuto",
"minutes": "{0} minuti",
"hour": "un'ora",
"hours": "{0} ore",
"day": "un giorno",
"days": "{0} giorni",
"week": "una settimana,",
"weeks": "{0} settimane",
"month": "un mese",
"months": "{0} mesi",
"year": "un anno",
"years": "{0} anni",
}
month_names = [
"",
"gennaio",
"febbraio",
"marzo",
"aprile",
"maggio",
"giugno",
"luglio",
"agosto",
"settembre",
"ottobre",
"novembre",
"dicembre",
]
month_abbreviations = [
"",
"gen",
"feb",
"mar",
"apr",
"mag",
"giu",
"lug",
"ago",
"set",
"ott",
"nov",
"dic",
]
day_names = [
"",
"lunedì",
"martedì",
"mercoledì",
"giovedì",
"venerdì",
"sabato",
"domenica",
]
day_abbreviations = ["", "lun", "mar", "mer", "gio", "ven", "sab", "dom"]
ordinal_day_re = r"((?P[1-3]?[0-9](?=[ºª]))[ºª])"
def _ordinal_number(self, n: int) -> str:
return f"{n}º"
class SpanishLocale(Locale):
names = ["es", "es-es"]
past = "hace {0}"
future = "en {0}"
and_word = "y"
timeframes = {
"now": "ahora",
"second": "un segundo",
"seconds": "{0} segundos",
"minute": "un minuto",
"minutes": "{0} minutos",
"hour": "una hora",
"hours": "{0} horas",
"day": "un día",
"days": "{0} días",
"week": "una semana",
"weeks": "{0} semanas",
"month": "un mes",
"months": "{0} meses",
"year": "un año",
"years": "{0} años",
}
meridians = {"am": "am", "pm": "pm", "AM": "AM", "PM": "PM"}
month_names = [
"",
"enero",
"febrero",
"marzo",
"abril",
"mayo",
"junio",
"julio",
"agosto",
"septiembre",
"octubre",
"noviembre",
"diciembre",
]
month_abbreviations = [
"",
"ene",
"feb",
"mar",
"abr",
"may",
"jun",
"jul",
"ago",
"sep",
"oct",
"nov",
"dic",
]
day_names = [
"",
"lunes",
"martes",
"miércoles",
"jueves",
"viernes",
"sábado",
"domingo",
]
day_abbreviations = ["", "lun", "mar", "mie", "jue", "vie", "sab", "dom"]
ordinal_day_re = r"((?P[1-3]?[0-9](?=[ºª]))[ºª])"
def _ordinal_number(self, n: int) -> str:
return f"{n}º"
class FrenchBaseLocale(Locale):
past = "il y a {0}"
future = "dans {0}"
and_word = "et"
timeframes = {
"now": "maintenant",
"second": "une seconde",
"seconds": "{0} secondes",
"minute": "une minute",
"minutes": "{0} minutes",
"hour": "une heure",
"hours": "{0} heures",
"day": "un jour",
"days": "{0} jours",
"week": "une semaine",
"weeks": "{0} semaines",
"month": "un mois",
"months": "{0} mois",
"year": "un an",
"years": "{0} ans",
}
month_names = [
"",
"janvier",
"février",
"mars",
"avril",
"mai",
"juin",
"juillet",
"août",
"septembre",
"octobre",
"novembre",
"décembre",
]
day_names = [
"",
"lundi",
"mardi",
"mercredi",
"jeudi",
"vendredi",
"samedi",
"dimanche",
]
day_abbreviations = ["", "lun", "mar", "mer", "jeu", "ven", "sam", "dim"]
ordinal_day_re = (
r"((?P\b1(?=er\b)|[1-3]?[02-9](?=e\b)|[1-3]1(?=e\b))(er|e)\b)"
)
def _ordinal_number(self, n: int) -> str:
if abs(n) == 1:
return f"{n}er"
return f"{n}e"
class FrenchLocale(FrenchBaseLocale, Locale):
names = ["fr", "fr-fr"]
month_abbreviations = [
"",
"janv",
"févr",
"mars",
"avr",
"mai",
"juin",
"juil",
"août",
"sept",
"oct",
"nov",
"déc",
]
class FrenchCanadianLocale(FrenchBaseLocale, Locale):
names = ["fr-ca"]
month_abbreviations = [
"",
"janv",
"févr",
"mars",
"avr",
"mai",
"juin",
"juill",
"août",
"sept",
"oct",
"nov",
"déc",
]
class GreekLocale(Locale):
names = ["el", "el-gr"]
past = "{0} πριν"
future = "σε {0}"
and_word = "και"
timeframes = {
"now": "τώρα",
"second": "ένα δεύτερο",
"seconds": "{0} δευτερόλεπτα",
"minute": "ένα λεπτό",
"minutes": "{0} λεπτά",
"hour": "μία ώρα",
"hours": "{0} ώρες",
"day": "μία μέρα",
"days": "{0} μέρες",
"week": "μία εβδομάδα",
"weeks": "{0} εβδομάδες",
"month": "ένα μήνα",
"months": "{0} μήνες",
"year": "ένα χρόνο",
"years": "{0} χρόνια",
}
month_names = [
"",
"Ιανουαρίου",
"Φεβρουαρίου",
"Μαρτίου",
"Απριλίου",
"Μαΐου",
"Ιουνίου",
"Ιουλίου",
"Αυγούστου",
"Σεπτεμβρίου",
"Οκτωβρίου",
"Νοεμβρίου",
"Δεκεμβρίου",
]
month_abbreviations = [
"",
"Ιαν",
"Φεβ",
"Μαρ",
"Απρ",
"Μαϊ",
"Ιον",
"Ιολ",
"Αυγ",
"Σεπ",
"Οκτ",
"Νοε",
"Δεκ",
]
day_names = [
"",
"Δευτέρα",
"Τρίτη",
"Τετάρτη",
"Πέμπτη",
"Παρασκευή",
"Σάββατο",
"Κυριακή",
]
day_abbreviations = ["", "Δευ", "Τρι", "Τετ", "Πεμ", "Παρ", "Σαβ", "Κυρ"]
class JapaneseLocale(Locale):
names = ["ja", "ja-jp"]
past = "{0}前"
future = "{0}後"
and_word = ""
timeframes = {
"now": "現在",
"second": "1秒",
"seconds": "{0}秒",
"minute": "1分",
"minutes": "{0}分",
"hour": "1時間",
"hours": "{0}時間",
"day": "1日",
"days": "{0}日",
"week": "1週間",
"weeks": "{0}週間",
"month": "1ヶ月",
"months": "{0}ヶ月",
"year": "1年",
"years": "{0}年",
}
month_names = [
"",
"1月",
"2月",
"3月",
"4月",
"5月",
"6月",
"7月",
"8月",
"9月",
"10月",
"11月",
"12月",
]
month_abbreviations = [
"",
" 1",
" 2",
" 3",
" 4",
" 5",
" 6",
" 7",
" 8",
" 9",
"10",
"11",
"12",
]
day_names = ["", "月曜日", "火曜日", "水曜日", "木曜日", "金曜日", "土曜日", "日曜日"]
day_abbreviations = ["", "月", "火", "水", "木", "金", "土", "日"]
class SwedishLocale(Locale):
names = ["sv", "sv-se"]
past = "för {0} sen"
future = "om {0}"
and_word = "och"
timeframes = {
"now": "just nu",
"second": "en sekund",
"seconds": "{0} sekunder",
"minute": "en minut",
"minutes": "{0} minuter",
"hour": "en timme",
"hours": "{0} timmar",
"day": "en dag",
"days": "{0} dagar",
"week": "en vecka",
"weeks": "{0} veckor",
"month": "en månad",
"months": "{0} månader",
"year": "ett år",
"years": "{0} år",
}
month_names = [
"",
"januari",
"februari",
"mars",
"april",
"maj",
"juni",
"juli",
"augusti",
"september",
"oktober",
"november",
"december",
]
month_abbreviations = [
"",
"jan",
"feb",
"mar",
"apr",
"maj",
"jun",
"jul",
"aug",
"sep",
"okt",
"nov",
"dec",
]
day_names = [
"",
"måndag",
"tisdag",
"onsdag",
"torsdag",
"fredag",
"lördag",
"söndag",
]
day_abbreviations = ["", "mån", "tis", "ons", "tor", "fre", "lör", "sön"]
class FinnishLocale(Locale):
names = ["fi", "fi-fi"]
# The finnish grammar is very complex, and its hard to convert
# 1-to-1 to something like English.
past = "{0} sitten"
future = "{0} kuluttua"
timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]] = {
"now": "juuri nyt",
"second": "sekunti",
"seconds": {"past": "{0} muutama sekunti", "future": "{0} muutaman sekunnin"},
"minute": {"past": "minuutti", "future": "minuutin"},
"minutes": {"past": "{0} minuuttia", "future": "{0} minuutin"},
"hour": {"past": "tunti", "future": "tunnin"},
"hours": {"past": "{0} tuntia", "future": "{0} tunnin"},
"day": "päivä",
"days": {"past": "{0} päivää", "future": "{0} päivän"},
"month": {"past": "kuukausi", "future": "kuukauden"},
"months": {"past": "{0} kuukautta", "future": "{0} kuukauden"},
"year": {"past": "vuosi", "future": "vuoden"},
"years": {"past": "{0} vuotta", "future": "{0} vuoden"},
}
# Months and days are lowercase in Finnish
month_names = [
"",
"tammikuu",
"helmikuu",
"maaliskuu",
"huhtikuu",
"toukokuu",
"kesäkuu",
"heinäkuu",
"elokuu",
"syyskuu",
"lokakuu",
"marraskuu",
"joulukuu",
]
month_abbreviations = [
"",
"tammi",
"helmi",
"maalis",
"huhti",
"touko",
"kesä",
"heinä",
"elo",
"syys",
"loka",
"marras",
"joulu",
]
day_names = [
"",
"maanantai",
"tiistai",
"keskiviikko",
"torstai",
"perjantai",
"lauantai",
"sunnuntai",
]
day_abbreviations = ["", "ma", "ti", "ke", "to", "pe", "la", "su"]
def _format_timeframe(self, timeframe: TimeFrameLiteral, delta: int) -> str:
form = self.timeframes[timeframe]
if isinstance(form, Mapping):
if delta < 0:
form = form["past"]
else:
form = form["future"]
return form.format(abs(delta))
def _ordinal_number(self, n: int) -> str:
return f"{n}."
class ChineseCNLocale(Locale):
names = ["zh", "zh-cn"]
past = "{0}前"
future = "{0}后"
timeframes = {
"now": "刚才",
"second": "一秒",
"seconds": "{0}秒",
"minute": "1分钟",
"minutes": "{0}分钟",
"hour": "1小时",
"hours": "{0}小时",
"day": "1天",
"days": "{0}天",
"week": "一周",
"weeks": "{0}周",
"month": "1个月",
"months": "{0}个月",
"year": "1年",
"years": "{0}年",
}
month_names = [
"",
"一月",
"二月",
"三月",
"四月",
"五月",
"六月",
"七月",
"八月",
"九月",
"十月",
"十一月",
"十二月",
]
month_abbreviations = [
"",
" 1",
" 2",
" 3",
" 4",
" 5",
" 6",
" 7",
" 8",
" 9",
"10",
"11",
"12",
]
day_names = ["", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日"]
day_abbreviations = ["", "一", "二", "三", "四", "五", "六", "日"]
class ChineseTWLocale(Locale):
names = ["zh-tw"]
past = "{0}前"
future = "{0}後"
and_word = "和"
timeframes = {
"now": "剛才",
"second": "1秒",
"seconds": "{0}秒",
"minute": "1分鐘",
"minutes": "{0}分鐘",
"hour": "1小時",
"hours": "{0}小時",
"day": "1天",
"days": "{0}天",
"week": "1週",
"weeks": "{0}週",
"month": "1個月",
"months": "{0}個月",
"year": "1年",
"years": "{0}年",
}
month_names = [
"",
"1月",
"2月",
"3月",
"4月",
"5月",
"6月",
"7月",
"8月",
"9月",
"10月",
"11月",
"12月",
]
month_abbreviations = [
"",
" 1",
" 2",
" 3",
" 4",
" 5",
" 6",
" 7",
" 8",
" 9",
"10",
"11",
"12",
]
day_names = ["", "週一", "週二", "週三", "週四", "週五", "週六", "週日"]
day_abbreviations = ["", "一", "二", "三", "四", "五", "六", "日"]
class HongKongLocale(Locale):
names = ["zh-hk"]
past = "{0}前"
future = "{0}後"
timeframes = {
"now": "剛才",
"second": "1秒",
"seconds": "{0}秒",
"minute": "1分鐘",
"minutes": "{0}分鐘",
"hour": "1小時",
"hours": "{0}小時",
"day": "1天",
"days": "{0}天",
"week": "1星期",
"weeks": "{0}星期",
"month": "1個月",
"months": "{0}個月",
"year": "1年",
"years": "{0}年",
}
month_names = [
"",
"1月",
"2月",
"3月",
"4月",
"5月",
"6月",
"7月",
"8月",
"9月",
"10月",
"11月",
"12月",
]
month_abbreviations = [
"",
" 1",
" 2",
" 3",
" 4",
" 5",
" 6",
" 7",
" 8",
" 9",
"10",
"11",
"12",
]
day_names = ["", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日"]
day_abbreviations = ["", "一", "二", "三", "四", "五", "六", "日"]
class KoreanLocale(Locale):
names = ["ko", "ko-kr"]
past = "{0} 전"
future = "{0} 후"
timeframes = {
"now": "지금",
"second": "1초",
"seconds": "{0}초",
"minute": "1분",
"minutes": "{0}분",
"hour": "한시간",
"hours": "{0}시간",
"day": "하루",
"days": "{0}일",
"week": "1주",
"weeks": "{0}주",
"month": "한달",
"months": "{0}개월",
"year": "1년",
"years": "{0}년",
}
special_dayframes = {
-3: "그끄제",
-2: "그제",
-1: "어제",
1: "내일",
2: "모레",
3: "글피",
4: "그글피",
}
special_yearframes = {-2: "제작년", -1: "작년", 1: "내년", 2: "내후년"}
month_names = [
"",
"1월",
"2월",
"3월",
"4월",
"5월",
"6월",
"7월",
"8월",
"9월",
"10월",
"11월",
"12월",
]
month_abbreviations = [
"",
" 1",
" 2",
" 3",
" 4",
" 5",
" 6",
" 7",
" 8",
" 9",
"10",
"11",
"12",
]
day_names = ["", "월요일", "화요일", "수요일", "목요일", "금요일", "토요일", "일요일"]
day_abbreviations = ["", "월", "화", "수", "목", "금", "토", "일"]
def _ordinal_number(self, n: int) -> str:
ordinals = ["0", "첫", "두", "세", "네", "다섯", "여섯", "일곱", "여덟", "아홉", "열"]
if n < len(ordinals):
return f"{ordinals[n]}번째"
return f"{n}번째"
def _format_relative(
self,
humanized: str,
timeframe: TimeFrameLiteral,
delta: Union[float, int],
) -> str:
if timeframe in ("day", "days"):
special = self.special_dayframes.get(int(delta))
if special:
return special
elif timeframe in ("year", "years"):
special = self.special_yearframes.get(int(delta))
if special:
return special
return super()._format_relative(humanized, timeframe, delta)
# derived locale types & implementations.
class DutchLocale(Locale):
names = ["nl", "nl-nl"]
past = "{0} geleden"
future = "over {0}"
timeframes = {
"now": "nu",
"second": "een seconde",
"seconds": "{0} seconden",
"minute": "een minuut",
"minutes": "{0} minuten",
"hour": "een uur",
"hours": "{0} uur",
"day": "een dag",
"days": "{0} dagen",
"week": "een week",
"weeks": "{0} weken",
"month": "een maand",
"months": "{0} maanden",
"year": "een jaar",
"years": "{0} jaar",
}
# In Dutch names of months and days are not starting with a capital letter
# like in the English language.
month_names = [
"",
"januari",
"februari",
"maart",
"april",
"mei",
"juni",
"juli",
"augustus",
"september",
"oktober",
"november",
"december",
]
month_abbreviations = [
"",
"jan",
"feb",
"mrt",
"apr",
"mei",
"jun",
"jul",
"aug",
"sep",
"okt",
"nov",
"dec",
]
day_names = [
"",
"maandag",
"dinsdag",
"woensdag",
"donderdag",
"vrijdag",
"zaterdag",
"zondag",
]
day_abbreviations = ["", "ma", "di", "wo", "do", "vr", "za", "zo"]
class SlavicBaseLocale(Locale):
timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]]
def _format_timeframe(self, timeframe: TimeFrameLiteral, delta: int) -> str:
form = self.timeframes[timeframe]
delta = abs(delta)
if isinstance(form, Mapping):
if delta % 10 == 1 and delta % 100 != 11:
form = form["singular"]
elif 2 <= delta % 10 <= 4 and (delta % 100 < 10 or delta % 100 >= 20):
form = form["dual"]
else:
form = form["plural"]
return form.format(delta)
class BelarusianLocale(SlavicBaseLocale):
names = ["be", "be-by"]
past = "{0} таму"
future = "праз {0}"
timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]] = {
"now": "зараз",
"second": "секунду",
"seconds": "{0} некалькі секунд",
"minute": "хвіліну",
"minutes": {
"singular": "{0} хвіліну",
"dual": "{0} хвіліны",
"plural": "{0} хвілін",
},
"hour": "гадзіну",
"hours": {
"singular": "{0} гадзіну",
"dual": "{0} гадзіны",
"plural": "{0} гадзін",
},
"day": "дзень",
"days": {"singular": "{0} дзень", "dual": "{0} дні", "plural": "{0} дзён"},
"month": "месяц",
"months": {
"singular": "{0} месяц",
"dual": "{0} месяцы",
"plural": "{0} месяцаў",
},
"year": "год",
"years": {"singular": "{0} год", "dual": "{0} гады", "plural": "{0} гадоў"},
}
month_names = [
"",
"студзеня",
"лютага",
"сакавіка",
"красавіка",
"траўня",
"чэрвеня",
"ліпеня",
"жніўня",
"верасня",
"кастрычніка",
"лістапада",
"снежня",
]
month_abbreviations = [
"",
"студ",
"лют",
"сак",
"крас",
"трав",
"чэрв",
"ліп",
"жнів",
"вер",
"каст",
"ліст",
"снеж",
]
day_names = [
"",
"панядзелак",
"аўторак",
"серада",
"чацвер",
"пятніца",
"субота",
"нядзеля",
]
day_abbreviations = ["", "пн", "ат", "ср", "чц", "пт", "сб", "нд"]
class PolishLocale(SlavicBaseLocale):
names = ["pl", "pl-pl"]
past = "{0} temu"
future = "za {0}"
# The nouns should be in genitive case (Polish: "dopełniacz")
# in order to correctly form `past` & `future` expressions.
timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]] = {
"now": "teraz",
"second": "sekundę",
"seconds": {
"singular": "{0} sekund",
"dual": "{0} sekundy",
"plural": "{0} sekund",
},
"minute": "minutę",
"minutes": {
"singular": "{0} minut",
"dual": "{0} minuty",
"plural": "{0} minut",
},
"hour": "godzinę",
"hours": {
"singular": "{0} godzin",
"dual": "{0} godziny",
"plural": "{0} godzin",
},
"day": "dzień",
"days": "{0} dni",
"week": "tydzień",
"weeks": {
"singular": "{0} tygodni",
"dual": "{0} tygodnie",
"plural": "{0} tygodni",
},
"month": "miesiąc",
"months": {
"singular": "{0} miesięcy",
"dual": "{0} miesiące",
"plural": "{0} miesięcy",
},
"year": "rok",
"years": {"singular": "{0} lat", "dual": "{0} lata", "plural": "{0} lat"},
}
month_names = [
"",
"styczeń",
"luty",
"marzec",
"kwiecień",
"maj",
"czerwiec",
"lipiec",
"sierpień",
"wrzesień",
"październik",
"listopad",
"grudzień",
]
month_abbreviations = [
"",
"sty",
"lut",
"mar",
"kwi",
"maj",
"cze",
"lip",
"sie",
"wrz",
"paź",
"lis",
"gru",
]
day_names = [
"",
"poniedziałek",
"wtorek",
"środa",
"czwartek",
"piątek",
"sobota",
"niedziela",
]
day_abbreviations = ["", "Pn", "Wt", "Śr", "Czw", "Pt", "So", "Nd"]
class RussianLocale(SlavicBaseLocale):
names = ["ru", "ru-ru"]
past = "{0} назад"
future = "через {0}"
timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]] = {
"now": "сейчас",
"second": "Второй",
"seconds": "{0} несколько секунд",
"minute": "минуту",
"minutes": {
"singular": "{0} минуту",
"dual": "{0} минуты",
"plural": "{0} минут",
},
"hour": "час",
"hours": {"singular": "{0} час", "dual": "{0} часа", "plural": "{0} часов"},
"day": "день",
"days": {"singular": "{0} день", "dual": "{0} дня", "plural": "{0} дней"},
"week": "неделю",
"weeks": {
"singular": "{0} неделю",
"dual": "{0} недели",
"plural": "{0} недель",
},
"month": "месяц",
"months": {
"singular": "{0} месяц",
"dual": "{0} месяца",
"plural": "{0} месяцев",
},
"year": "год",
"years": {"singular": "{0} год", "dual": "{0} года", "plural": "{0} лет"},
}
month_names = [
"",
"января",
"февраля",
"марта",
"апреля",
"мая",
"июня",
"июля",
"августа",
"сентября",
"октября",
"ноября",
"декабря",
]
month_abbreviations = [
"",
"янв",
"фев",
"мар",
"апр",
"май",
"июн",
"июл",
"авг",
"сен",
"окт",
"ноя",
"дек",
]
day_names = [
"",
"понедельник",
"вторник",
"среда",
"четверг",
"пятница",
"суббота",
"воскресенье",
]
day_abbreviations = ["", "пн", "вт", "ср", "чт", "пт", "сб", "вс"]
class AfrikaansLocale(Locale):
names = ["af", "af-nl"]
past = "{0} gelede"
future = "in {0}"
timeframes = {
"now": "nou",
"second": "n sekonde",
"seconds": "{0} sekondes",
"minute": "minuut",
"minutes": "{0} minute",
"hour": "uur",
"hours": "{0} ure",
"day": "een dag",
"days": "{0} dae",
"month": "een maand",
"months": "{0} maande",
"year": "een jaar",
"years": "{0} jaar",
}
month_names = [
"",
"Januarie",
"Februarie",
"Maart",
"April",
"Mei",
"Junie",
"Julie",
"Augustus",
"September",
"Oktober",
"November",
"Desember",
]
month_abbreviations = [
"",
"Jan",
"Feb",
"Mrt",
"Apr",
"Mei",
"Jun",
"Jul",
"Aug",
"Sep",
"Okt",
"Nov",
"Des",
]
day_names = [
"",
"Maandag",
"Dinsdag",
"Woensdag",
"Donderdag",
"Vrydag",
"Saterdag",
"Sondag",
]
day_abbreviations = ["", "Ma", "Di", "Wo", "Do", "Vr", "Za", "So"]
class BulgarianLocale(SlavicBaseLocale):
names = ["bg", "bg-bg"]
past = "{0} назад"
future = "напред {0}"
timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]] = {
"now": "сега",
"second": "секунда",
"seconds": "{0} няколко секунди",
"minute": "минута",
"minutes": {
"singular": "{0} минута",
"dual": "{0} минути",
"plural": "{0} минути",
},
"hour": "час",
"hours": {"singular": "{0} час", "dual": "{0} часа", "plural": "{0} часа"},
"day": "ден",
"days": {"singular": "{0} ден", "dual": "{0} дни", "plural": "{0} дни"},
"month": "месец",
"months": {
"singular": "{0} месец",
"dual": "{0} месеца",
"plural": "{0} месеца",
},
"year": "година",
"years": {
"singular": "{0} година",
"dual": "{0} години",
"plural": "{0} години",
},
}
month_names = [
"",
"януари",
"февруари",
"март",
"април",
"май",
"юни",
"юли",
"август",
"септември",
"октомври",
"ноември",
"декември",
]
month_abbreviations = [
"",
"ян",
"февр",
"март",
"апр",
"май",
"юни",
"юли",
"авг",
"септ",
"окт",
"ноем",
"дек",
]
day_names = [
"",
"понеделник",
"вторник",
"сряда",
"четвъртък",
"петък",
"събота",
"неделя",
]
day_abbreviations = ["", "пон", "вт", "ср", "четв", "пет", "съб", "нед"]
class UkrainianLocale(SlavicBaseLocale):
names = ["ua", "uk", "uk-ua"]
past = "{0} тому"
future = "за {0}"
timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]] = {
"now": "зараз",
"second": "секунда",
"seconds": "{0} кілька секунд",
"minute": "хвилину",
"minutes": {
"singular": "{0} хвилину",
"dual": "{0} хвилини",
"plural": "{0} хвилин",
},
"hour": "годину",
"hours": {
"singular": "{0} годину",
"dual": "{0} години",
"plural": "{0} годин",
},
"day": "день",
"days": {"singular": "{0} день", "dual": "{0} дні", "plural": "{0} днів"},
"month": "місяць",
"months": {
"singular": "{0} місяць",
"dual": "{0} місяці",
"plural": "{0} місяців",
},
"year": "рік",
"years": {"singular": "{0} рік", "dual": "{0} роки", "plural": "{0} років"},
}
month_names = [
"",
"січня",
"лютого",
"березня",
"квітня",
"травня",
"червня",
"липня",
"серпня",
"вересня",
"жовтня",
"листопада",
"грудня",
]
month_abbreviations = [
"",
"січ",
"лют",
"бер",
"квіт",
"трав",
"черв",
"лип",
"серп",
"вер",
"жовт",
"лист",
"груд",
]
day_names = [
"",
"понеділок",
"вівторок",
"середа",
"четвер",
"п’ятниця",
"субота",
"неділя",
]
day_abbreviations = ["", "пн", "вт", "ср", "чт", "пт", "сб", "нд"]
class MacedonianLocale(SlavicBaseLocale):
names = ["mk", "mk-mk"]
past = "пред {0}"
future = "за {0}"
timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]] = {
"now": "сега",
"second": "една секунда",
"seconds": {
"singular": "{0} секунда",
"dual": "{0} секунди",
"plural": "{0} секунди",
},
"minute": "една минута",
"minutes": {
"singular": "{0} минута",
"dual": "{0} минути",
"plural": "{0} минути",
},
"hour": "еден саат",
"hours": {"singular": "{0} саат", "dual": "{0} саати", "plural": "{0} саати"},
"day": "еден ден",
"days": {"singular": "{0} ден", "dual": "{0} дена", "plural": "{0} дена"},
"week": "една недела",
"weeks": {
"singular": "{0} недела",
"dual": "{0} недели",
"plural": "{0} недели",
},
"month": "еден месец",
"months": {
"singular": "{0} месец",
"dual": "{0} месеци",
"plural": "{0} месеци",
},
"year": "една година",
"years": {
"singular": "{0} година",
"dual": "{0} години",
"plural": "{0} години",
},
}
meridians = {"am": "дп", "pm": "пп", "AM": "претпладне", "PM": "попладне"}
month_names = [
"",
"Јануари",
"Февруари",
"Март",
"Април",
"Мај",
"Јуни",
"Јули",
"Август",
"Септември",
"Октомври",
"Ноември",
"Декември",
]
month_abbreviations = [
"",
"Јан",
"Фев",
"Мар",
"Апр",
"Мај",
"Јун",
"Јул",
"Авг",
"Септ",
"Окт",
"Ноем",
"Декем",
]
day_names = [
"",
"Понеделник",
"Вторник",
"Среда",
"Четврток",
"Петок",
"Сабота",
"Недела",
]
day_abbreviations = [
"",
"Пон",
"Вт",
"Сре",
"Чет",
"Пет",
"Саб",
"Нед",
]
class GermanBaseLocale(Locale):
past = "vor {0}"
future = "in {0}"
and_word = "und"
timeframes = {
"now": "gerade eben",
"second": "einer Sekunde",
"seconds": "{0} Sekunden",
"minute": "einer Minute",
"minutes": "{0} Minuten",
"hour": "einer Stunde",
"hours": "{0} Stunden",
"day": "einem Tag",
"days": "{0} Tagen",
"week": "einer Woche",
"weeks": "{0} Wochen",
"month": "einem Monat",
"months": "{0} Monaten",
"year": "einem Jahr",
"years": "{0} Jahren",
}
timeframes_only_distance = timeframes.copy()
timeframes_only_distance["second"] = "eine Sekunde"
timeframes_only_distance["minute"] = "eine Minute"
timeframes_only_distance["hour"] = "eine Stunde"
timeframes_only_distance["day"] = "ein Tag"
timeframes_only_distance["days"] = "{0} Tage"
timeframes_only_distance["week"] = "eine Woche"
timeframes_only_distance["month"] = "ein Monat"
timeframes_only_distance["months"] = "{0} Monate"
timeframes_only_distance["year"] = "ein Jahr"
timeframes_only_distance["years"] = "{0} Jahre"
month_names = [
"",
"Januar",
"Februar",
"März",
"April",
"Mai",
"Juni",
"Juli",
"August",
"September",
"Oktober",
"November",
"Dezember",
]
month_abbreviations = [
"",
"Jan",
"Feb",
"Mär",
"Apr",
"Mai",
"Jun",
"Jul",
"Aug",
"Sep",
"Okt",
"Nov",
"Dez",
]
day_names = [
"",
"Montag",
"Dienstag",
"Mittwoch",
"Donnerstag",
"Freitag",
"Samstag",
"Sonntag",
]
day_abbreviations = ["", "Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"]
def _ordinal_number(self, n: int) -> str:
return f"{n}."
def describe(
self,
timeframe: TimeFrameLiteral,
delta: Union[int, float] = 0,
only_distance: bool = False,
) -> str:
"""Describes a delta within a timeframe in plain language.
:param timeframe: a string representing a timeframe.
:param delta: a quantity representing a delta in a timeframe.
:param only_distance: return only distance eg: "11 seconds" without "in" or "ago" keywords
"""
if not only_distance:
return super().describe(timeframe, delta, only_distance)
# German uses a different case without 'in' or 'ago'
humanized = self.timeframes_only_distance[timeframe].format(trunc(abs(delta)))
return humanized
class GermanLocale(GermanBaseLocale, Locale):
names = ["de", "de-de"]
class SwissLocale(GermanBaseLocale, Locale):
names = ["de-ch"]
class AustrianLocale(GermanBaseLocale, Locale):
names = ["de-at"]
month_names = [
"",
"Jänner",
"Februar",
"März",
"April",
"Mai",
"Juni",
"Juli",
"August",
"September",
"Oktober",
"November",
"Dezember",
]
class NorwegianLocale(Locale):
names = ["nb", "nb-no"]
past = "for {0} siden"
future = "om {0}"
timeframes = {
"now": "nå nettopp",
"second": "ett sekund",
"seconds": "{0} sekunder",
"minute": "ett minutt",
"minutes": "{0} minutter",
"hour": "en time",
"hours": "{0} timer",
"day": "en dag",
"days": "{0} dager",
"month": "en måned",
"months": "{0} måneder",
"year": "ett år",
"years": "{0} år",
}
month_names = [
"",
"januar",
"februar",
"mars",
"april",
"mai",
"juni",
"juli",
"august",
"september",
"oktober",
"november",
"desember",
]
month_abbreviations = [
"",
"jan",
"feb",
"mar",
"apr",
"mai",
"jun",
"jul",
"aug",
"sep",
"okt",
"nov",
"des",
]
day_names = [
"",
"mandag",
"tirsdag",
"onsdag",
"torsdag",
"fredag",
"lørdag",
"søndag",
]
day_abbreviations = ["", "ma", "ti", "on", "to", "fr", "lø", "sø"]
class NewNorwegianLocale(Locale):
names = ["nn", "nn-no"]
past = "for {0} sidan"
future = "om {0}"
timeframes = {
"now": "no nettopp",
"second": "eitt sekund",
"seconds": "{0} sekund",
"minute": "eitt minutt",
"minutes": "{0} minutt",
"hour": "ein time",
"hours": "{0} timar",
"day": "ein dag",
"days": "{0} dagar",
"month": "en månad",
"months": "{0} månader",
"year": "eitt år",
"years": "{0} år",
}
month_names = [
"",
"januar",
"februar",
"mars",
"april",
"mai",
"juni",
"juli",
"august",
"september",
"oktober",
"november",
"desember",
]
month_abbreviations = [
"",
"jan",
"feb",
"mar",
"apr",
"mai",
"jun",
"jul",
"aug",
"sep",
"okt",
"nov",
"des",
]
day_names = [
"",
"måndag",
"tysdag",
"onsdag",
"torsdag",
"fredag",
"laurdag",
"sundag",
]
day_abbreviations = ["", "må", "ty", "on", "to", "fr", "la", "su"]
class PortugueseLocale(Locale):
names = ["pt", "pt-pt"]
past = "há {0}"
future = "em {0}"
and_word = "e"
timeframes = {
"now": "agora",
"second": "um segundo",
"seconds": "{0} segundos",
"minute": "um minuto",
"minutes": "{0} minutos",
"hour": "uma hora",
"hours": "{0} horas",
"day": "um dia",
"days": "{0} dias",
"week": "uma semana",
"weeks": "{0} semanas",
"month": "um mês",
"months": "{0} meses",
"year": "um ano",
"years": "{0} anos",
}
month_names = [
"",
"Janeiro",
"Fevereiro",
"Março",
"Abril",
"Maio",
"Junho",
"Julho",
"Agosto",
"Setembro",
"Outubro",
"Novembro",
"Dezembro",
]
month_abbreviations = [
"",
"Jan",
"Fev",
"Mar",
"Abr",
"Mai",
"Jun",
"Jul",
"Ago",
"Set",
"Out",
"Nov",
"Dez",
]
day_names = [
"",
"Segunda-feira",
"Terça-feira",
"Quarta-feira",
"Quinta-feira",
"Sexta-feira",
"Sábado",
"Domingo",
]
day_abbreviations = ["", "Seg", "Ter", "Qua", "Qui", "Sex", "Sab", "Dom"]
class BrazilianPortugueseLocale(PortugueseLocale):
names = ["pt-br"]
past = "faz {0}"
class TagalogLocale(Locale):
names = ["tl", "tl-ph"]
past = "nakaraang {0}"
future = "{0} mula ngayon"
timeframes = {
"now": "ngayon lang",
"second": "isang segundo",
"seconds": "{0} segundo",
"minute": "isang minuto",
"minutes": "{0} minuto",
"hour": "isang oras",
"hours": "{0} oras",
"day": "isang araw",
"days": "{0} araw",
"week": "isang linggo",
"weeks": "{0} linggo",
"month": "isang buwan",
"months": "{0} buwan",
"year": "isang taon",
"years": "{0} taon",
}
month_names = [
"",
"Enero",
"Pebrero",
"Marso",
"Abril",
"Mayo",
"Hunyo",
"Hulyo",
"Agosto",
"Setyembre",
"Oktubre",
"Nobyembre",
"Disyembre",
]
month_abbreviations = [
"",
"Ene",
"Peb",
"Mar",
"Abr",
"May",
"Hun",
"Hul",
"Ago",
"Set",
"Okt",
"Nob",
"Dis",
]
day_names = [
"",
"Lunes",
"Martes",
"Miyerkules",
"Huwebes",
"Biyernes",
"Sabado",
"Linggo",
]
day_abbreviations = ["", "Lun", "Mar", "Miy", "Huw", "Biy", "Sab", "Lin"]
meridians = {"am": "nu", "pm": "nh", "AM": "ng umaga", "PM": "ng hapon"}
def _ordinal_number(self, n: int) -> str:
return f"ika-{n}"
class VietnameseLocale(Locale):
names = ["vi", "vi-vn"]
past = "{0} trước"
future = "{0} nữa"
timeframes = {
"now": "hiện tại",
"second": "một giây",
"seconds": "{0} giây",
"minute": "một phút",
"minutes": "{0} phút",
"hour": "một giờ",
"hours": "{0} giờ",
"day": "một ngày",
"days": "{0} ngày",
"week": "một tuần",
"weeks": "{0} tuần",
"month": "một tháng",
"months": "{0} tháng",
"year": "một năm",
"years": "{0} năm",
}
month_names = [
"",
"Tháng Một",
"Tháng Hai",
"Tháng Ba",
"Tháng Tư",
"Tháng Năm",
"Tháng Sáu",
"Tháng Bảy",
"Tháng Tám",
"Tháng Chín",
"Tháng Mười",
"Tháng Mười Một",
"Tháng Mười Hai",
]
month_abbreviations = [
"",
"Tháng 1",
"Tháng 2",
"Tháng 3",
"Tháng 4",
"Tháng 5",
"Tháng 6",
"Tháng 7",
"Tháng 8",
"Tháng 9",
"Tháng 10",
"Tháng 11",
"Tháng 12",
]
day_names = [
"",
"Thứ Hai",
"Thứ Ba",
"Thứ Tư",
"Thứ Năm",
"Thứ Sáu",
"Thứ Bảy",
"Chủ Nhật",
]
day_abbreviations = ["", "Thứ 2", "Thứ 3", "Thứ 4", "Thứ 5", "Thứ 6", "Thứ 7", "CN"]
class TurkishLocale(Locale):
names = ["tr", "tr-tr"]
past = "{0} önce"
future = "{0} sonra"
and_word = "ve"
timeframes = {
"now": "şimdi",
"second": "bir saniye",
"seconds": "{0} saniye",
"minute": "bir dakika",
"minutes": "{0} dakika",
"hour": "bir saat",
"hours": "{0} saat",
"day": "bir gün",
"days": "{0} gün",
"week": "bir hafta",
"weeks": "{0} hafta",
"month": "bir ay",
"months": "{0} ay",
"year": "bir yıl",
"years": "{0} yıl",
}
meridians = {"am": "öö", "pm": "ös", "AM": "ÖÖ", "PM": "ÖS"}
month_names = [
"",
"Ocak",
"Şubat",
"Mart",
"Nisan",
"Mayıs",
"Haziran",
"Temmuz",
"Ağustos",
"Eylül",
"Ekim",
"Kasım",
"Aralık",
]
month_abbreviations = [
"",
"Oca",
"Şub",
"Mar",
"Nis",
"May",
"Haz",
"Tem",
"Ağu",
"Eyl",
"Eki",
"Kas",
"Ara",
]
day_names = [
"",
"Pazartesi",
"Salı",
"Çarşamba",
"Perşembe",
"Cuma",
"Cumartesi",
"Pazar",
]
day_abbreviations = ["", "Pzt", "Sal", "Çar", "Per", "Cum", "Cmt", "Paz"]
class AzerbaijaniLocale(Locale):
names = ["az", "az-az"]
past = "{0} əvvəl"
future = "{0} sonra"
timeframes = {
"now": "indi",
"second": "bir saniyə",
"seconds": "{0} saniyə",
"minute": "bir dəqiqə",
"minutes": "{0} dəqiqə",
"hour": "bir saat",
"hours": "{0} saat",
"day": "bir gün",
"days": "{0} gün",
"week": "bir həftə",
"weeks": "{0} həftə",
"month": "bir ay",
"months": "{0} ay",
"year": "bir il",
"years": "{0} il",
}
month_names = [
"",
"Yanvar",
"Fevral",
"Mart",
"Aprel",
"May",
"İyun",
"İyul",
"Avqust",
"Sentyabr",
"Oktyabr",
"Noyabr",
"Dekabr",
]
month_abbreviations = [
"",
"Yan",
"Fev",
"Mar",
"Apr",
"May",
"İyn",
"İyl",
"Avq",
"Sen",
"Okt",
"Noy",
"Dek",
]
day_names = [
"",
"Bazar ertəsi",
"Çərşənbə axşamı",
"Çərşənbə",
"Cümə axşamı",
"Cümə",
"Şənbə",
"Bazar",
]
day_abbreviations = ["", "Ber", "Çax", "Çər", "Cax", "Cüm", "Şnb", "Bzr"]
class ArabicLocale(Locale):
names = [
"ar",
"ar-ae",
"ar-bh",
"ar-dj",
"ar-eg",
"ar-eh",
"ar-er",
"ar-km",
"ar-kw",
"ar-ly",
"ar-om",
"ar-qa",
"ar-sa",
"ar-sd",
"ar-so",
"ar-ss",
"ar-td",
"ar-ye",
]
past = "منذ {0}"
future = "خلال {0}"
timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]] = {
"now": "الآن",
"second": "ثانية",
"seconds": {"2": "ثانيتين", "ten": "{0} ثوان", "higher": "{0} ثانية"},
"minute": "دقيقة",
"minutes": {"2": "دقيقتين", "ten": "{0} دقائق", "higher": "{0} دقيقة"},
"hour": "ساعة",
"hours": {"2": "ساعتين", "ten": "{0} ساعات", "higher": "{0} ساعة"},
"day": "يوم",
"days": {"2": "يومين", "ten": "{0} أيام", "higher": "{0} يوم"},
"month": "شهر",
"months": {"2": "شهرين", "ten": "{0} أشهر", "higher": "{0} شهر"},
"year": "سنة",
"years": {"2": "سنتين", "ten": "{0} سنوات", "higher": "{0} سنة"},
}
month_names = [
"",
"يناير",
"فبراير",
"مارس",
"أبريل",
"مايو",
"يونيو",
"يوليو",
"أغسطس",
"سبتمبر",
"أكتوبر",
"نوفمبر",
"ديسمبر",
]
month_abbreviations = [
"",
"يناير",
"فبراير",
"مارس",
"أبريل",
"مايو",
"يونيو",
"يوليو",
"أغسطس",
"سبتمبر",
"أكتوبر",
"نوفمبر",
"ديسمبر",
]
day_names = [
"",
"الإثنين",
"الثلاثاء",
"الأربعاء",
"الخميس",
"الجمعة",
"السبت",
"الأحد",
]
day_abbreviations = ["", "إثنين", "ثلاثاء", "أربعاء", "خميس", "جمعة", "سبت", "أحد"]
def _format_timeframe(self, timeframe: TimeFrameLiteral, delta: int) -> str:
form = self.timeframes[timeframe]
delta = abs(delta)
if isinstance(form, Mapping):
if delta == 2:
form = form["2"]
elif 2 < delta <= 10:
form = form["ten"]
else:
form = form["higher"]
return form.format(delta)
class LevantArabicLocale(ArabicLocale):
names = ["ar-iq", "ar-jo", "ar-lb", "ar-ps", "ar-sy"]
month_names = [
"",
"كانون الثاني",
"شباط",
"آذار",
"نيسان",
"أيار",
"حزيران",
"تموز",
"آب",
"أيلول",
"تشرين الأول",
"تشرين الثاني",
"كانون الأول",
]
month_abbreviations = [
"",
"كانون الثاني",
"شباط",
"آذار",
"نيسان",
"أيار",
"حزيران",
"تموز",
"آب",
"أيلول",
"تشرين الأول",
"تشرين الثاني",
"كانون الأول",
]
class AlgeriaTunisiaArabicLocale(ArabicLocale):
names = ["ar-tn", "ar-dz"]
month_names = [
"",
"جانفي",
"فيفري",
"مارس",
"أفريل",
"ماي",
"جوان",
"جويلية",
"أوت",
"سبتمبر",
"أكتوبر",
"نوفمبر",
"ديسمبر",
]
month_abbreviations = [
"",
"جانفي",
"فيفري",
"مارس",
"أفريل",
"ماي",
"جوان",
"جويلية",
"أوت",
"سبتمبر",
"أكتوبر",
"نوفمبر",
"ديسمبر",
]
class MauritaniaArabicLocale(ArabicLocale):
names = ["ar-mr"]
month_names = [
"",
"يناير",
"فبراير",
"مارس",
"إبريل",
"مايو",
"يونيو",
"يوليو",
"أغشت",
"شتمبر",
"أكتوبر",
"نوفمبر",
"دجمبر",
]
month_abbreviations = [
"",
"يناير",
"فبراير",
"مارس",
"إبريل",
"مايو",
"يونيو",
"يوليو",
"أغشت",
"شتمبر",
"أكتوبر",
"نوفمبر",
"دجمبر",
]
class MoroccoArabicLocale(ArabicLocale):
names = ["ar-ma"]
month_names = [
"",
"يناير",
"فبراير",
"مارس",
"أبريل",
"ماي",
"يونيو",
"يوليوز",
"غشت",
"شتنبر",
"أكتوبر",
"نونبر",
"دجنبر",
]
month_abbreviations = [
"",
"يناير",
"فبراير",
"مارس",
"أبريل",
"ماي",
"يونيو",
"يوليوز",
"غشت",
"شتنبر",
"أكتوبر",
"نونبر",
"دجنبر",
]
class IcelandicLocale(Locale):
def _format_timeframe(self, timeframe: TimeFrameLiteral, delta: int) -> str:
form = self.timeframes[timeframe]
if isinstance(form, Mapping):
if delta < 0:
form = form["past"]
elif delta > 0:
form = form["future"]
else:
raise ValueError(
"Icelandic Locale does not support units with a delta of zero. "
"Please consider making a contribution to fix this issue."
)
# FIXME: handle when delta is 0
return form.format(abs(delta))
names = ["is", "is-is"]
past = "fyrir {0} síðan"
future = "eftir {0}"
timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]] = {
"now": "rétt í þessu",
"second": {"past": "sekúndu", "future": "sekúndu"},
"seconds": {"past": "{0} nokkrum sekúndum", "future": "nokkrar sekúndur"},
"minute": {"past": "einni mínútu", "future": "eina mínútu"},
"minutes": {"past": "{0} mínútum", "future": "{0} mínútur"},
"hour": {"past": "einum tíma", "future": "einn tíma"},
"hours": {"past": "{0} tímum", "future": "{0} tíma"},
"day": {"past": "einum degi", "future": "einn dag"},
"days": {"past": "{0} dögum", "future": "{0} daga"},
"month": {"past": "einum mánuði", "future": "einn mánuð"},
"months": {"past": "{0} mánuðum", "future": "{0} mánuði"},
"year": {"past": "einu ári", "future": "eitt ár"},
"years": {"past": "{0} árum", "future": "{0} ár"},
}
meridians = {"am": "f.h.", "pm": "e.h.", "AM": "f.h.", "PM": "e.h."}
month_names = [
"",
"janúar",
"febrúar",
"mars",
"apríl",
"maí",
"júní",
"júlí",
"ágúst",
"september",
"október",
"nóvember",
"desember",
]
month_abbreviations = [
"",
"jan",
"feb",
"mar",
"apr",
"maí",
"jún",
"júl",
"ágú",
"sep",
"okt",
"nóv",
"des",
]
day_names = [
"",
"mánudagur",
"þriðjudagur",
"miðvikudagur",
"fimmtudagur",
"föstudagur",
"laugardagur",
"sunnudagur",
]
day_abbreviations = ["", "mán", "þri", "mið", "fim", "fös", "lau", "sun"]
class DanishLocale(Locale):
names = ["da", "da-dk"]
past = "for {0} siden"
future = "efter {0}"
and_word = "og"
timeframes = {
"now": "lige nu",
"second": "et sekund",
"seconds": "{0} et par sekunder",
"minute": "et minut",
"minutes": "{0} minutter",
"hour": "en time",
"hours": "{0} timer",
"day": "en dag",
"days": "{0} dage",
"month": "en måned",
"months": "{0} måneder",
"year": "et år",
"years": "{0} år",
}
month_names = [
"",
"januar",
"februar",
"marts",
"april",
"maj",
"juni",
"juli",
"august",
"september",
"oktober",
"november",
"december",
]
month_abbreviations = [
"",
"jan",
"feb",
"mar",
"apr",
"maj",
"jun",
"jul",
"aug",
"sep",
"okt",
"nov",
"dec",
]
day_names = [
"",
"mandag",
"tirsdag",
"onsdag",
"torsdag",
"fredag",
"lørdag",
"søndag",
]
day_abbreviations = ["", "man", "tir", "ons", "tor", "fre", "lør", "søn"]
class MalayalamLocale(Locale):
names = ["ml"]
past = "{0} മുമ്പ്"
future = "{0} ശേഷം"
timeframes = {
"now": "ഇപ്പോൾ",
"second": "ഒരു നിമിഷം",
"seconds": "{0} സെക്കന്റ്",
"minute": "ഒരു മിനിറ്റ്",
"minutes": "{0} മിനിറ്റ്",
"hour": "ഒരു മണിക്കൂർ",
"hours": "{0} മണിക്കൂർ",
"day": "ഒരു ദിവസം ",
"days": "{0} ദിവസം ",
"month": "ഒരു മാസം ",
"months": "{0} മാസം ",
"year": "ഒരു വർഷം ",
"years": "{0} വർഷം ",
}
meridians = {
"am": "രാവിലെ",
"pm": "ഉച്ചക്ക് ശേഷം",
"AM": "രാവിലെ",
"PM": "ഉച്ചക്ക് ശേഷം",
}
month_names = [
"",
"ജനുവരി",
"ഫെബ്രുവരി",
"മാർച്ച്",
"ഏപ്രിൽ ",
"മെയ് ",
"ജൂണ്",
"ജൂലൈ",
"ഓഗസ്റ്റ്",
"സെപ്റ്റംബർ",
"ഒക്ടോബർ",
"നവംബർ",
"ഡിസംബർ",
]
month_abbreviations = [
"",
"ജനു",
"ഫെബ് ",
"മാർ",
"ഏപ്രിൽ",
"മേയ്",
"ജൂണ്",
"ജൂലൈ",
"ഓഗസ്റ",
"സെപ്റ്റ",
"ഒക്ടോ",
"നവം",
"ഡിസം",
]
day_names = ["", "തിങ്കള്", "ചൊവ്വ", "ബുധന്", "വ്യാഴം", "വെള്ളി", "ശനി", "ഞായര്"]
day_abbreviations = [
"",
"തിങ്കള്",
"ചൊവ്വ",
"ബുധന്",
"വ്യാഴം",
"വെള്ളി",
"ശനി",
"ഞായര്",
]
class HindiLocale(Locale):
names = ["hi", "hi-in"]
past = "{0} पहले"
future = "{0} बाद"
timeframes = {
"now": "अभी",
"second": "एक पल",
"seconds": "{0} सेकंड्",
"minute": "एक मिनट ",
"minutes": "{0} मिनट ",
"hour": "एक घंटा",
"hours": "{0} घंटे",
"day": "एक दिन",
"days": "{0} दिन",
"month": "एक माह ",
"months": "{0} महीने ",
"year": "एक वर्ष ",
"years": "{0} साल ",
}
meridians = {"am": "सुबह", "pm": "शाम", "AM": "सुबह", "PM": "शाम"}
month_names = [
"",
"जनवरी",
"फरवरी",
"मार्च",
"अप्रैल ",
"मई",
"जून",
"जुलाई",
"अगस्त",
"सितंबर",
"अक्टूबर",
"नवंबर",
"दिसंबर",
]
month_abbreviations = [
"",
"जन",
"फ़र",
"मार्च",
"अप्रै",
"मई",
"जून",
"जुलाई",
"आग",
"सित",
"अकत",
"नवे",
"दिस",
]
day_names = [
"",
"सोमवार",
"मंगलवार",
"बुधवार",
"गुरुवार",
"शुक्रवार",
"शनिवार",
"रविवार",
]
day_abbreviations = ["", "सोम", "मंगल", "बुध", "गुरुवार", "शुक्र", "शनि", "रवि"]
class CzechLocale(Locale):
names = ["cs", "cs-cz"]
timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]] = {
"now": "Teď",
"second": {"past": "vteřina", "future": "vteřina", "zero": "vteřina"},
"seconds": {
"past": "{0} sekundami",
"future-singular": "{0} sekundy",
"future-paucal": "{0} sekund",
},
"minute": {"past": "minutou", "future": "minutu", "zero": "{0} minut"},
"minutes": {
"past": "{0} minutami",
"future-singular": "{0} minuty",
"future-paucal": "{0} minut",
},
"hour": {"past": "hodinou", "future": "hodinu", "zero": "{0} hodin"},
"hours": {
"past": "{0} hodinami",
"future-singular": "{0} hodiny",
"future-paucal": "{0} hodin",
},
"day": {"past": "dnem", "future": "den", "zero": "{0} dnů"},
"days": {
"past": "{0} dny",
"future-singular": "{0} dny",
"future-paucal": "{0} dnů",
},
"week": {"past": "týdnem", "future": "týden", "zero": "{0} týdnů"},
"weeks": {
"past": "{0} týdny",
"future-singular": "{0} týdny",
"future-paucal": "{0} týdnů",
},
"month": {"past": "měsícem", "future": "měsíc", "zero": "{0} měsíců"},
"months": {
"past": "{0} měsíci",
"future-singular": "{0} měsíce",
"future-paucal": "{0} měsíců",
},
"year": {"past": "rokem", "future": "rok", "zero": "{0} let"},
"years": {
"past": "{0} lety",
"future-singular": "{0} roky",
"future-paucal": "{0} let",
},
}
past = "Před {0}"
future = "Za {0}"
month_names = [
"",
"leden",
"únor",
"březen",
"duben",
"květen",
"červen",
"červenec",
"srpen",
"září",
"říjen",
"listopad",
"prosinec",
]
month_abbreviations = [
"",
"led",
"úno",
"bře",
"dub",
"kvě",
"čvn",
"čvc",
"srp",
"zář",
"říj",
"lis",
"pro",
]
day_names = [
"",
"pondělí",
"úterý",
"středa",
"čtvrtek",
"pátek",
"sobota",
"neděle",
]
day_abbreviations = ["", "po", "út", "st", "čt", "pá", "so", "ne"]
def _format_timeframe(self, timeframe: TimeFrameLiteral, delta: int) -> str:
"""Czech aware time frame format function, takes into account
the differences between past and future forms."""
abs_delta = abs(delta)
form = self.timeframes[timeframe]
if isinstance(form, str):
return form.format(abs_delta)
if delta == 0:
key = "zero" # And *never* use 0 in the singular!
elif delta < 0:
key = "past"
else:
# Needed since both regular future and future-singular and future-paucal cases
if "future-singular" not in form:
key = "future"
elif 2 <= abs_delta % 10 <= 4 and (
abs_delta % 100 < 10 or abs_delta % 100 >= 20
):
key = "future-singular"
else:
key = "future-paucal"
form: str = form[key]
return form.format(abs_delta)
class SlovakLocale(Locale):
names = ["sk", "sk-sk"]
timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]] = {
"now": "Teraz",
"second": {"past": "sekundou", "future": "sekundu", "zero": "{0} sekúnd"},
"seconds": {
"past": "{0} sekundami",
"future-singular": "{0} sekundy",
"future-paucal": "{0} sekúnd",
},
"minute": {"past": "minútou", "future": "minútu", "zero": "{0} minút"},
"minutes": {
"past": "{0} minútami",
"future-singular": "{0} minúty",
"future-paucal": "{0} minút",
},
"hour": {"past": "hodinou", "future": "hodinu", "zero": "{0} hodín"},
"hours": {
"past": "{0} hodinami",
"future-singular": "{0} hodiny",
"future-paucal": "{0} hodín",
},
"day": {"past": "dňom", "future": "deň", "zero": "{0} dní"},
"days": {
"past": "{0} dňami",
"future-singular": "{0} dni",
"future-paucal": "{0} dní",
},
"week": {"past": "týždňom", "future": "týždeň", "zero": "{0} týždňov"},
"weeks": {
"past": "{0} týždňami",
"future-singular": "{0} týždne",
"future-paucal": "{0} týždňov",
},
"month": {"past": "mesiacom", "future": "mesiac", "zero": "{0} mesiacov"},
"months": {
"past": "{0} mesiacmi",
"future-singular": "{0} mesiace",
"future-paucal": "{0} mesiacov",
},
"year": {"past": "rokom", "future": "rok", "zero": "{0} rokov"},
"years": {
"past": "{0} rokmi",
"future-singular": "{0} roky",
"future-paucal": "{0} rokov",
},
}
past = "Pred {0}"
future = "O {0}"
and_word = "a"
month_names = [
"",
"január",
"február",
"marec",
"apríl",
"máj",
"jún",
"júl",
"august",
"september",
"október",
"november",
"december",
]
month_abbreviations = [
"",
"jan",
"feb",
"mar",
"apr",
"máj",
"jún",
"júl",
"aug",
"sep",
"okt",
"nov",
"dec",
]
day_names = [
"",
"pondelok",
"utorok",
"streda",
"štvrtok",
"piatok",
"sobota",
"nedeľa",
]
day_abbreviations = ["", "po", "ut", "st", "št", "pi", "so", "ne"]
def _format_timeframe(self, timeframe: TimeFrameLiteral, delta: int) -> str:
"""Slovak aware time frame format function, takes into account
the differences between past and future forms."""
abs_delta = abs(delta)
form = self.timeframes[timeframe]
if isinstance(form, str):
return form.format(abs_delta)
if delta == 0:
key = "zero" # And *never* use 0 in the singular!
elif delta < 0:
key = "past"
else:
if "future-singular" not in form:
key = "future"
elif 2 <= abs_delta % 10 <= 4 and (
abs_delta % 100 < 10 or abs_delta % 100 >= 20
):
key = "future-singular"
else:
key = "future-paucal"
form: str = form[key]
return form.format(abs_delta)
class FarsiLocale(Locale):
names = ["fa", "fa-ir"]
past = "{0} قبل"
future = "در {0}"
timeframes = {
"now": "اکنون",
"second": "یک لحظه",
"seconds": "{0} ثانیه",
"minute": "یک دقیقه",
"minutes": "{0} دقیقه",
"hour": "یک ساعت",
"hours": "{0} ساعت",
"day": "یک روز",
"days": "{0} روز",
"month": "یک ماه",
"months": "{0} ماه",
"year": "یک سال",
"years": "{0} سال",
}
meridians = {
"am": "قبل از ظهر",
"pm": "بعد از ظهر",
"AM": "قبل از ظهر",
"PM": "بعد از ظهر",
}
month_names = [
"",
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December",
]
month_abbreviations = [
"",
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec",
]
day_names = [
"",
"دو شنبه",
"سه شنبه",
"چهارشنبه",
"پنجشنبه",
"جمعه",
"شنبه",
"یکشنبه",
]
day_abbreviations = ["", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
class HebrewLocale(Locale):
names = ["he", "he-il"]
past = "לפני {0}"
future = "בעוד {0}"
and_word = "ו"
timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]] = {
"now": "הרגע",
"second": "שנייה",
"seconds": "{0} שניות",
"minute": "דקה",
"minutes": "{0} דקות",
"hour": "שעה",
"hours": {"2": "שעתיים", "ten": "{0} שעות", "higher": "{0} שעות"},
"day": "יום",
"days": {"2": "יומיים", "ten": "{0} ימים", "higher": "{0} יום"},
"week": "שבוע",
"weeks": {"2": "שבועיים", "ten": "{0} שבועות", "higher": "{0} שבועות"},
"month": "חודש",
"months": {"2": "חודשיים", "ten": "{0} חודשים", "higher": "{0} חודשים"},
"year": "שנה",
"years": {"2": "שנתיים", "ten": "{0} שנים", "higher": "{0} שנה"},
}
meridians = {
"am": 'לפנ"צ',
"pm": 'אחר"צ',
"AM": "לפני הצהריים",
"PM": "אחרי הצהריים",
}
month_names = [
"",
"ינואר",
"פברואר",
"מרץ",
"אפריל",
"מאי",
"יוני",
"יולי",
"אוגוסט",
"ספטמבר",
"אוקטובר",
"נובמבר",
"דצמבר",
]
month_abbreviations = [
"",
"ינו׳",
"פבר׳",
"מרץ",
"אפר׳",
"מאי",
"יוני",
"יולי",
"אוג׳",
"ספט׳",
"אוק׳",
"נוב׳",
"דצמ׳",
]
day_names = ["", "שני", "שלישי", "רביעי", "חמישי", "שישי", "שבת", "ראשון"]
day_abbreviations = ["", "ב׳", "ג׳", "ד׳", "ה׳", "ו׳", "ש׳", "א׳"]
def _format_timeframe(self, timeframe: TimeFrameLiteral, delta: int) -> str:
form = self.timeframes[timeframe]
delta = abs(delta)
if isinstance(form, Mapping):
if delta == 2:
form = form["2"]
elif delta == 0 or 2 < delta <= 10:
form = form["ten"]
else:
form = form["higher"]
return form.format(delta)
def describe_multi(
self,
timeframes: Sequence[Tuple[TimeFrameLiteral, Union[int, float]]],
only_distance: bool = False,
) -> str:
"""Describes a delta within multiple timeframes in plain language.
In Hebrew, the and word behaves a bit differently.
:param timeframes: a list of string, quantity pairs each representing a timeframe and delta.
:param only_distance: return only distance eg: "2 hours and 11 seconds" without "in" or "ago" keywords
"""
humanized = ""
for index, (timeframe, delta) in enumerate(timeframes):
last_humanized = self._format_timeframe(timeframe, trunc(delta))
if index == 0:
humanized = last_humanized
elif index == len(timeframes) - 1: # Must have at least 2 items
humanized += " " + self.and_word
if last_humanized[0].isdecimal():
humanized += "־"
humanized += last_humanized
else: # Don't add for the last one
humanized += ", " + last_humanized
if not only_distance:
humanized = self._format_relative(humanized, timeframe, trunc(delta))
return humanized
class MarathiLocale(Locale):
names = ["mr"]
past = "{0} आधी"
future = "{0} नंतर"
timeframes = {
"now": "सद्य",
"second": "एक सेकंद",
"seconds": "{0} सेकंद",
"minute": "एक मिनिट ",
"minutes": "{0} मिनिट ",
"hour": "एक तास",
"hours": "{0} तास",
"day": "एक दिवस",
"days": "{0} दिवस",
"month": "एक महिना ",
"months": "{0} महिने ",
"year": "एक वर्ष ",
"years": "{0} वर्ष ",
}
meridians = {"am": "सकाळ", "pm": "संध्याकाळ", "AM": "सकाळ", "PM": "संध्याकाळ"}
month_names = [
"",
"जानेवारी",
"फेब्रुवारी",
"मार्च",
"एप्रिल",
"मे",
"जून",
"जुलै",
"अॉगस्ट",
"सप्टेंबर",
"अॉक्टोबर",
"नोव्हेंबर",
"डिसेंबर",
]
month_abbreviations = [
"",
"जान",
"फेब्रु",
"मार्च",
"एप्रि",
"मे",
"जून",
"जुलै",
"अॉग",
"सप्टें",
"अॉक्टो",
"नोव्हें",
"डिसें",
]
day_names = [
"",
"सोमवार",
"मंगळवार",
"बुधवार",
"गुरुवार",
"शुक्रवार",
"शनिवार",
"रविवार",
]
day_abbreviations = ["", "सोम", "मंगळ", "बुध", "गुरु", "शुक्र", "शनि", "रवि"]
class CatalanLocale(Locale):
names = ["ca", "ca-es", "ca-ad", "ca-fr", "ca-it"]
past = "Fa {0}"
future = "En {0}"
and_word = "i"
timeframes = {
"now": "Ara mateix",
"second": "un segon",
"seconds": "{0} segons",
"minute": "un minut",
"minutes": "{0} minuts",
"hour": "una hora",
"hours": "{0} hores",
"day": "un dia",
"days": "{0} dies",
"month": "un mes",
"months": "{0} mesos",
"year": "un any",
"years": "{0} anys",
}
month_names = [
"",
"gener",
"febrer",
"març",
"abril",
"maig",
"juny",
"juliol",
"agost",
"setembre",
"octubre",
"novembre",
"desembre",
]
month_abbreviations = [
"",
"gen.",
"febr.",
"març",
"abr.",
"maig",
"juny",
"jul.",
"ag.",
"set.",
"oct.",
"nov.",
"des.",
]
day_names = [
"",
"dilluns",
"dimarts",
"dimecres",
"dijous",
"divendres",
"dissabte",
"diumenge",
]
day_abbreviations = [
"",
"dl.",
"dt.",
"dc.",
"dj.",
"dv.",
"ds.",
"dg.",
]
class BasqueLocale(Locale):
names = ["eu", "eu-eu"]
past = "duela {0}"
future = "{0}" # I don't know what's the right phrase in Basque for the future.
timeframes = {
"now": "Orain",
"second": "segundo bat",
"seconds": "{0} segundu",
"minute": "minutu bat",
"minutes": "{0} minutu",
"hour": "ordu bat",
"hours": "{0} ordu",
"day": "egun bat",
"days": "{0} egun",
"month": "hilabete bat",
"months": "{0} hilabet",
"year": "urte bat",
"years": "{0} urte",
}
month_names = [
"",
"urtarrilak",
"otsailak",
"martxoak",
"apirilak",
"maiatzak",
"ekainak",
"uztailak",
"abuztuak",
"irailak",
"urriak",
"azaroak",
"abenduak",
]
month_abbreviations = [
"",
"urt",
"ots",
"mar",
"api",
"mai",
"eka",
"uzt",
"abu",
"ira",
"urr",
"aza",
"abe",
]
day_names = [
"",
"astelehena",
"asteartea",
"asteazkena",
"osteguna",
"ostirala",
"larunbata",
"igandea",
]
day_abbreviations = ["", "al", "ar", "az", "og", "ol", "lr", "ig"]
class HungarianLocale(Locale):
names = ["hu", "hu-hu"]
past = "{0} ezelőtt"
future = "{0} múlva"
timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]] = {
"now": "éppen most",
"second": {"past": "egy második", "future": "egy második"},
"seconds": {"past": "{0} másodpercekkel", "future": "{0} pár másodperc"},
"minute": {"past": "egy perccel", "future": "egy perc"},
"minutes": {"past": "{0} perccel", "future": "{0} perc"},
"hour": {"past": "egy órával", "future": "egy óra"},
"hours": {"past": "{0} órával", "future": "{0} óra"},
"day": {"past": "egy nappal", "future": "egy nap"},
"days": {"past": "{0} nappal", "future": "{0} nap"},
"month": {"past": "egy hónappal", "future": "egy hónap"},
"months": {"past": "{0} hónappal", "future": "{0} hónap"},
"year": {"past": "egy évvel", "future": "egy év"},
"years": {"past": "{0} évvel", "future": "{0} év"},
}
month_names = [
"",
"január",
"február",
"március",
"április",
"május",
"június",
"július",
"augusztus",
"szeptember",
"október",
"november",
"december",
]
month_abbreviations = [
"",
"jan",
"febr",
"márc",
"ápr",
"máj",
"jún",
"júl",
"aug",
"szept",
"okt",
"nov",
"dec",
]
day_names = [
"",
"hétfő",
"kedd",
"szerda",
"csütörtök",
"péntek",
"szombat",
"vasárnap",
]
day_abbreviations = ["", "hét", "kedd", "szer", "csüt", "pént", "szom", "vas"]
meridians = {"am": "de", "pm": "du", "AM": "DE", "PM": "DU"}
def _format_timeframe(self, timeframe: TimeFrameLiteral, delta: int) -> str:
form = self.timeframes[timeframe]
if isinstance(form, Mapping):
if delta > 0:
form = form["future"]
else:
form = form["past"]
return form.format(abs(delta))
class EsperantoLocale(Locale):
names = ["eo", "eo-xx"]
past = "antaŭ {0}"
future = "post {0}"
timeframes = {
"now": "nun",
"second": "sekundo",
"seconds": "{0} kelkaj sekundoj",
"minute": "unu minuto",
"minutes": "{0} minutoj",
"hour": "un horo",
"hours": "{0} horoj",
"day": "unu tago",
"days": "{0} tagoj",
"month": "unu monato",
"months": "{0} monatoj",
"year": "unu jaro",
"years": "{0} jaroj",
}
month_names = [
"",
"januaro",
"februaro",
"marto",
"aprilo",
"majo",
"junio",
"julio",
"aŭgusto",
"septembro",
"oktobro",
"novembro",
"decembro",
]
month_abbreviations = [
"",
"jan",
"feb",
"mar",
"apr",
"maj",
"jun",
"jul",
"aŭg",
"sep",
"okt",
"nov",
"dec",
]
day_names = [
"",
"lundo",
"mardo",
"merkredo",
"ĵaŭdo",
"vendredo",
"sabato",
"dimanĉo",
]
day_abbreviations = ["", "lun", "mar", "mer", "ĵaŭ", "ven", "sab", "dim"]
meridians = {"am": "atm", "pm": "ptm", "AM": "ATM", "PM": "PTM"}
ordinal_day_re = r"((?P[1-3]?[0-9](?=a))a)"
def _ordinal_number(self, n: int) -> str:
return f"{n}a"
class ThaiLocale(Locale):
names = ["th", "th-th"]
past = "{0} ที่ผ่านมา"
future = "ในอีก {0}"
timeframes = {
"now": "ขณะนี้",
"second": "วินาที",
"seconds": "{0} ไม่กี่วินาที",
"minute": "1 นาที",
"minutes": "{0} นาที",
"hour": "1 ชั่วโมง",
"hours": "{0} ชั่วโมง",
"day": "1 วัน",
"days": "{0} วัน",
"month": "1 เดือน",
"months": "{0} เดือน",
"year": "1 ปี",
"years": "{0} ปี",
}
month_names = [
"",
"มกราคม",
"กุมภาพันธ์",
"มีนาคม",
"เมษายน",
"พฤษภาคม",
"มิถุนายน",
"กรกฎาคม",
"สิงหาคม",
"กันยายน",
"ตุลาคม",
"พฤศจิกายน",
"ธันวาคม",
]
month_abbreviations = [
"",
"ม.ค.",
"ก.พ.",
"มี.ค.",
"เม.ย.",
"พ.ค.",
"มิ.ย.",
"ก.ค.",
"ส.ค.",
"ก.ย.",
"ต.ค.",
"พ.ย.",
"ธ.ค.",
]
day_names = ["", "จันทร์", "อังคาร", "พุธ", "พฤหัสบดี", "ศุกร์", "เสาร์", "อาทิตย์"]
day_abbreviations = ["", "จ", "อ", "พ", "พฤ", "ศ", "ส", "อา"]
meridians = {"am": "am", "pm": "pm", "AM": "AM", "PM": "PM"}
BE_OFFSET = 543
def year_full(self, year: int) -> str:
"""Thai always use Buddhist Era (BE) which is CE + 543"""
year += self.BE_OFFSET
return f"{year:04d}"
def year_abbreviation(self, year: int) -> str:
"""Thai always use Buddhist Era (BE) which is CE + 543"""
year += self.BE_OFFSET
return f"{year:04d}"[2:]
def _format_relative(
self,
humanized: str,
timeframe: TimeFrameLiteral,
delta: Union[float, int],
) -> str:
"""Thai normally doesn't have any space between words"""
if timeframe == "now":
return humanized
direction = self.past if delta < 0 else self.future
relative_string = direction.format(humanized)
if timeframe == "seconds":
relative_string = relative_string.replace(" ", "")
return relative_string
class BengaliLocale(Locale):
names = ["bn", "bn-bd", "bn-in"]
past = "{0} আগে"
future = "{0} পরে"
timeframes = {
"now": "এখন",
"second": "একটি দ্বিতীয়",
"seconds": "{0} সেকেন্ড",
"minute": "এক মিনিট",
"minutes": "{0} মিনিট",
"hour": "এক ঘণ্টা",
"hours": "{0} ঘণ্টা",
"day": "এক দিন",
"days": "{0} দিন",
"month": "এক মাস",
"months": "{0} মাস ",
"year": "এক বছর",
"years": "{0} বছর",
}
meridians = {"am": "সকাল", "pm": "বিকাল", "AM": "সকাল", "PM": "বিকাল"}
month_names = [
"",
"জানুয়ারি",
"ফেব্রুয়ারি",
"মার্চ",
"এপ্রিল",
"মে",
"জুন",
"জুলাই",
"আগস্ট",
"সেপ্টেম্বর",
"অক্টোবর",
"নভেম্বর",
"ডিসেম্বর",
]
month_abbreviations = [
"",
"জানু",
"ফেব",
"মার্চ",
"এপ্রি",
"মে",
"জুন",
"জুল",
"অগা",
"সেপ্ট",
"অক্টো",
"নভে",
"ডিসে",
]
day_names = [
"",
"সোমবার",
"মঙ্গলবার",
"বুধবার",
"বৃহস্পতিবার",
"শুক্রবার",
"শনিবার",
"রবিবার",
]
day_abbreviations = ["", "সোম", "মঙ্গল", "বুধ", "বৃহঃ", "শুক্র", "শনি", "রবি"]
def _ordinal_number(self, n: int) -> str:
if n > 10 or n == 0:
return f"{n}তম"
if n in [1, 5, 7, 8, 9, 10]:
return f"{n}ম"
if n in [2, 3]:
return f"{n}য়"
if n == 4:
return f"{n}র্থ"
if n == 6:
return f"{n}ষ্ঠ"
class RomanshLocale(Locale):
names = ["rm", "rm-ch"]
past = "avant {0}"
future = "en {0}"
timeframes = {
"now": "en quest mument",
"second": "in secunda",
"seconds": "{0} secundas",
"minute": "ina minuta",
"minutes": "{0} minutas",
"hour": "in'ura",
"hours": "{0} ura",
"day": "in di",
"days": "{0} dis",
"month": "in mais",
"months": "{0} mais",
"year": "in onn",
"years": "{0} onns",
}
month_names = [
"",
"schaner",
"favrer",
"mars",
"avrigl",
"matg",
"zercladur",
"fanadur",
"avust",
"settember",
"october",
"november",
"december",
]
month_abbreviations = [
"",
"schan",
"fav",
"mars",
"avr",
"matg",
"zer",
"fan",
"avu",
"set",
"oct",
"nov",
"dec",
]
day_names = [
"",
"glindesdi",
"mardi",
"mesemna",
"gievgia",
"venderdi",
"sonda",
"dumengia",
]
day_abbreviations = ["", "gli", "ma", "me", "gie", "ve", "so", "du"]
class RomanianLocale(Locale):
names = ["ro", "ro-ro"]
past = "{0} în urmă"
future = "peste {0}"
and_word = "și"
timeframes = {
"now": "acum",
"second": "o secunda",
"seconds": "{0} câteva secunde",
"minute": "un minut",
"minutes": "{0} minute",
"hour": "o oră",
"hours": "{0} ore",
"day": "o zi",
"days": "{0} zile",
"month": "o lună",
"months": "{0} luni",
"year": "un an",
"years": "{0} ani",
}
month_names = [
"",
"ianuarie",
"februarie",
"martie",
"aprilie",
"mai",
"iunie",
"iulie",
"august",
"septembrie",
"octombrie",
"noiembrie",
"decembrie",
]
month_abbreviations = [
"",
"ian",
"febr",
"mart",
"apr",
"mai",
"iun",
"iul",
"aug",
"sept",
"oct",
"nov",
"dec",
]
day_names = [
"",
"luni",
"marți",
"miercuri",
"joi",
"vineri",
"sâmbătă",
"duminică",
]
day_abbreviations = ["", "Lun", "Mar", "Mie", "Joi", "Vin", "Sâm", "Dum"]
class SlovenianLocale(Locale):
names = ["sl", "sl-si"]
past = "pred {0}"
future = "čez {0}"
and_word = "in"
timeframes = {
"now": "zdaj",
"second": "sekundo",
"seconds": "{0} sekund",
"minute": "minuta",
"minutes": "{0} minutami",
"hour": "uro",
"hours": "{0} ur",
"day": "dan",
"days": "{0} dni",
"month": "mesec",
"months": "{0} mesecev",
"year": "leto",
"years": "{0} let",
}
meridians = {"am": "", "pm": "", "AM": "", "PM": ""}
month_names = [
"",
"Januar",
"Februar",
"Marec",
"April",
"Maj",
"Junij",
"Julij",
"Avgust",
"September",
"Oktober",
"November",
"December",
]
month_abbreviations = [
"",
"Jan",
"Feb",
"Mar",
"Apr",
"Maj",
"Jun",
"Jul",
"Avg",
"Sep",
"Okt",
"Nov",
"Dec",
]
day_names = [
"",
"Ponedeljek",
"Torek",
"Sreda",
"Četrtek",
"Petek",
"Sobota",
"Nedelja",
]
day_abbreviations = ["", "Pon", "Tor", "Sre", "Čet", "Pet", "Sob", "Ned"]
class IndonesianLocale(Locale):
names = ["id", "id-id"]
past = "{0} yang lalu"
future = "dalam {0}"
and_word = "dan"
timeframes = {
"now": "baru saja",
"second": "1 sebentar",
"seconds": "{0} detik",
"minute": "1 menit",
"minutes": "{0} menit",
"hour": "1 jam",
"hours": "{0} jam",
"day": "1 hari",
"days": "{0} hari",
"month": "1 bulan",
"months": "{0} bulan",
"year": "1 tahun",
"years": "{0} tahun",
}
meridians = {"am": "", "pm": "", "AM": "", "PM": ""}
month_names = [
"",
"Januari",
"Februari",
"Maret",
"April",
"Mei",
"Juni",
"Juli",
"Agustus",
"September",
"Oktober",
"November",
"Desember",
]
month_abbreviations = [
"",
"Jan",
"Feb",
"Mar",
"Apr",
"Mei",
"Jun",
"Jul",
"Ags",
"Sept",
"Okt",
"Nov",
"Des",
]
day_names = ["", "Senin", "Selasa", "Rabu", "Kamis", "Jumat", "Sabtu", "Minggu"]
day_abbreviations = [
"",
"Senin",
"Selasa",
"Rabu",
"Kamis",
"Jumat",
"Sabtu",
"Minggu",
]
class NepaliLocale(Locale):
names = ["ne", "ne-np"]
past = "{0} पहिले"
future = "{0} पछी"
timeframes = {
"now": "अहिले",
"second": "एक सेकेन्ड",
"seconds": "{0} सेकण्ड",
"minute": "मिनेट",
"minutes": "{0} मिनेट",
"hour": "एक घण्टा",
"hours": "{0} घण्टा",
"day": "एक दिन",
"days": "{0} दिन",
"month": "एक महिना",
"months": "{0} महिना",
"year": "एक बर्ष",
"years": "{0} बर्ष",
}
meridians = {"am": "पूर्वाह्न", "pm": "अपरान्ह", "AM": "पूर्वाह्न", "PM": "अपरान्ह"}
month_names = [
"",
"जनवरी",
"फेब्रुअरी",
"मार्च",
"एप्रील",
"मे",
"जुन",
"जुलाई",
"अगष्ट",
"सेप्टेम्बर",
"अक्टोबर",
"नोवेम्बर",
"डिसेम्बर",
]
month_abbreviations = [
"",
"जन",
"फेब",
"मार्च",
"एप्रील",
"मे",
"जुन",
"जुलाई",
"अग",
"सेप",
"अक्ट",
"नोव",
"डिस",
]
day_names = [
"",
"सोमवार",
"मंगलवार",
"बुधवार",
"बिहिवार",
"शुक्रवार",
"शनिवार",
"आइतवार",
]
day_abbreviations = ["", "सोम", "मंगल", "बुध", "बिहि", "शुक्र", "शनि", "आइत"]
class EstonianLocale(Locale):
names = ["ee", "et"]
past = "{0} tagasi"
future = "{0} pärast"
and_word = "ja"
timeframes: ClassVar[Mapping[TimeFrameLiteral, Mapping[str, str]]] = {
"now": {"past": "just nüüd", "future": "just nüüd"},
"second": {"past": "üks sekund", "future": "ühe sekundi"},
"seconds": {"past": "{0} sekundit", "future": "{0} sekundi"},
"minute": {"past": "üks minut", "future": "ühe minuti"},
"minutes": {"past": "{0} minutit", "future": "{0} minuti"},
"hour": {"past": "tund aega", "future": "tunni aja"},
"hours": {"past": "{0} tundi", "future": "{0} tunni"},
"day": {"past": "üks päev", "future": "ühe päeva"},
"days": {"past": "{0} päeva", "future": "{0} päeva"},
"month": {"past": "üks kuu", "future": "ühe kuu"},
"months": {"past": "{0} kuud", "future": "{0} kuu"},
"year": {"past": "üks aasta", "future": "ühe aasta"},
"years": {"past": "{0} aastat", "future": "{0} aasta"},
}
month_names = [
"",
"Jaanuar",
"Veebruar",
"Märts",
"Aprill",
"Mai",
"Juuni",
"Juuli",
"August",
"September",
"Oktoober",
"November",
"Detsember",
]
month_abbreviations = [
"",
"Jan",
"Veb",
"Mär",
"Apr",
"Mai",
"Jun",
"Jul",
"Aug",
"Sep",
"Okt",
"Nov",
"Dets",
]
day_names = [
"",
"Esmaspäev",
"Teisipäev",
"Kolmapäev",
"Neljapäev",
"Reede",
"Laupäev",
"Pühapäev",
]
day_abbreviations = ["", "Esm", "Teis", "Kolm", "Nelj", "Re", "Lau", "Püh"]
def _format_timeframe(self, timeframe: TimeFrameLiteral, delta: int) -> str:
form = self.timeframes[timeframe]
if delta > 0:
_form = form["future"]
else:
_form = form["past"]
return _form.format(abs(delta))
class LatvianLocale(Locale):
names = ["lv", "lv-lv"]
past = "pirms {0}"
future = "pēc {0}"
and_word = "un"
timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]] = {
"now": "tagad",
"second": "sekundes",
"seconds": "{0} sekundēm",
"minute": "minūtes",
"minutes": "{0} minūtēm",
"hour": "stundas",
"hours": "{0} stundām",
"day": "dienas",
"days": "{0} dienām",
"week": "nedēļas",
"weeks": "{0} nedēļām",
"month": "mēneša",
"months": "{0} mēnešiem",
"year": "gada",
"years": "{0} gadiem",
}
month_names = [
"",
"janvāris",
"februāris",
"marts",
"aprīlis",
"maijs",
"jūnijs",
"jūlijs",
"augusts",
"septembris",
"oktobris",
"novembris",
"decembris",
]
month_abbreviations = [
"",
"jan",
"feb",
"marts",
"apr",
"maijs",
"jūnijs",
"jūlijs",
"aug",
"sept",
"okt",
"nov",
"dec",
]
day_names = [
"",
"pirmdiena",
"otrdiena",
"trešdiena",
"ceturtdiena",
"piektdiena",
"sestdiena",
"svētdiena",
]
day_abbreviations = [
"",
"pi",
"ot",
"tr",
"ce",
"pi",
"se",
"sv",
]
class SwahiliLocale(Locale):
names = [
"sw",
"sw-ke",
"sw-tz",
]
past = "{0} iliyopita"
future = "muda wa {0}"
and_word = "na"
timeframes = {
"now": "sasa hivi",
"second": "sekunde",
"seconds": "sekunde {0}",
"minute": "dakika moja",
"minutes": "dakika {0}",
"hour": "saa moja",
"hours": "saa {0}",
"day": "siku moja",
"days": "siku {0}",
"week": "wiki moja",
"weeks": "wiki {0}",
"month": "mwezi moja",
"months": "miezi {0}",
"year": "mwaka moja",
"years": "miaka {0}",
}
meridians = {"am": "asu", "pm": "mch", "AM": "ASU", "PM": "MCH"}
month_names = [
"",
"Januari",
"Februari",
"Machi",
"Aprili",
"Mei",
"Juni",
"Julai",
"Agosti",
"Septemba",
"Oktoba",
"Novemba",
"Desemba",
]
month_abbreviations = [
"",
"Jan",
"Feb",
"Mac",
"Apr",
"Mei",
"Jun",
"Jul",
"Ago",
"Sep",
"Okt",
"Nov",
"Des",
]
day_names = [
"",
"Jumatatu",
"Jumanne",
"Jumatano",
"Alhamisi",
"Ijumaa",
"Jumamosi",
"Jumapili",
]
day_abbreviations = [
"",
"Jumatatu",
"Jumanne",
"Jumatano",
"Alhamisi",
"Ijumaa",
"Jumamosi",
"Jumapili",
]
class CroatianLocale(Locale):
names = ["hr", "hr-hr"]
past = "prije {0}"
future = "za {0}"
and_word = "i"
timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]] = {
"now": "upravo sad",
"second": "sekundu",
"seconds": {"double": "{0} sekunde", "higher": "{0} sekundi"},
"minute": "minutu",
"minutes": {"double": "{0} minute", "higher": "{0} minuta"},
"hour": "sat",
"hours": {"double": "{0} sata", "higher": "{0} sati"},
"day": "jedan dan",
"days": {"double": "{0} dana", "higher": "{0} dana"},
"week": "tjedan",
"weeks": {"double": "{0} tjedna", "higher": "{0} tjedana"},
"month": "mjesec",
"months": {"double": "{0} mjeseca", "higher": "{0} mjeseci"},
"year": "godinu",
"years": {"double": "{0} godine", "higher": "{0} godina"},
}
month_names = [
"",
"siječanj",
"veljača",
"ožujak",
"travanj",
"svibanj",
"lipanj",
"srpanj",
"kolovoz",
"rujan",
"listopad",
"studeni",
"prosinac",
]
month_abbreviations = [
"",
"siječ",
"velj",
"ožuj",
"trav",
"svib",
"lip",
"srp",
"kol",
"ruj",
"list",
"stud",
"pros",
]
day_names = [
"",
"ponedjeljak",
"utorak",
"srijeda",
"četvrtak",
"petak",
"subota",
"nedjelja",
]
day_abbreviations = [
"",
"po",
"ut",
"sr",
"če",
"pe",
"su",
"ne",
]
def _format_timeframe(self, timeframe: TimeFrameLiteral, delta: int) -> str:
form = self.timeframes[timeframe]
delta = abs(delta)
if isinstance(form, Mapping):
if 1 < delta <= 4:
form = form["double"]
else:
form = form["higher"]
return form.format(delta)
class LatinLocale(Locale):
names = ["la", "la-va"]
past = "ante {0}"
future = "in {0}"
and_word = "et"
timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]] = {
"now": "nunc",
"second": "secundum",
"seconds": "{0} secundis",
"minute": "minutam",
"minutes": "{0} minutis",
"hour": "horam",
"hours": "{0} horas",
"day": "diem",
"days": "{0} dies",
"week": "hebdomadem",
"weeks": "{0} hebdomades",
"month": "mensem",
"months": "{0} mensis",
"year": "annum",
"years": "{0} annos",
}
month_names = [
"",
"Ianuarius",
"Februarius",
"Martius",
"Aprilis",
"Maius",
"Iunius",
"Iulius",
"Augustus",
"September",
"October",
"November",
"December",
]
month_abbreviations = [
"",
"Ian",
"Febr",
"Mart",
"Apr",
"Mai",
"Iun",
"Iul",
"Aug",
"Sept",
"Oct",
"Nov",
"Dec",
]
day_names = [
"",
"dies Lunae",
"dies Martis",
"dies Mercurii",
"dies Iovis",
"dies Veneris",
"dies Saturni",
"dies Solis",
]
day_abbreviations = [
"",
"dies Lunae",
"dies Martis",
"dies Mercurii",
"dies Iovis",
"dies Veneris",
"dies Saturni",
"dies Solis",
]
class LithuanianLocale(Locale):
names = ["lt", "lt-lt"]
past = "prieš {0}"
future = "po {0}"
and_word = "ir"
timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]] = {
"now": "dabar",
"second": "sekundės",
"seconds": "{0} sekundžių",
"minute": "minutės",
"minutes": "{0} minučių",
"hour": "valandos",
"hours": "{0} valandų",
"day": "dieną",
"days": "{0} dienų",
"week": "savaitės",
"weeks": "{0} savaičių",
"month": "mėnesio",
"months": "{0} mėnesių",
"year": "metų",
"years": "{0} metų",
}
month_names = [
"",
"sausis",
"vasaris",
"kovas",
"balandis",
"gegužė",
"birželis",
"liepa",
"rugpjūtis",
"rugsėjis",
"spalis",
"lapkritis",
"gruodis",
]
month_abbreviations = [
"",
"saus",
"vas",
"kovas",
"bal",
"geg",
"birž",
"liepa",
"rugp",
"rugs",
"spalis",
"lapkr",
"gr",
]
day_names = [
"",
"pirmadienis",
"antradienis",
"trečiadienis",
"ketvirtadienis",
"penktadienis",
"šeštadienis",
"sekmadienis",
]
day_abbreviations = [
"",
"pi",
"an",
"tr",
"ke",
"pe",
"še",
"se",
]
class MalayLocale(Locale):
names = ["ms", "ms-my", "ms-bn"]
past = "{0} yang lalu"
future = "dalam {0}"
and_word = "dan"
timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]] = {
"now": "sekarang",
"second": "saat",
"seconds": "{0} saat",
"minute": "minit",
"minutes": "{0} minit",
"hour": "jam",
"hours": "{0} jam",
"day": "hari",
"days": "{0} hari",
"week": "minggu",
"weeks": "{0} minggu",
"month": "bulan",
"months": "{0} bulan",
"year": "tahun",
"years": "{0} tahun",
}
month_names = [
"",
"Januari",
"Februari",
"Mac",
"April",
"Mei",
"Jun",
"Julai",
"Ogos",
"September",
"Oktober",
"November",
"Disember",
]
month_abbreviations = [
"",
"Jan.",
"Feb.",
"Mac",
"Apr.",
"Mei",
"Jun",
"Julai",
"Og.",
"Sept.",
"Okt.",
"Nov.",
"Dis.",
]
day_names = [
"",
"Isnin",
"Selasa",
"Rabu",
"Khamis",
"Jumaat",
"Sabtu",
"Ahad",
]
day_abbreviations = [
"",
"Isnin",
"Selasa",
"Rabu",
"Khamis",
"Jumaat",
"Sabtu",
"Ahad",
]
class MalteseLocale(Locale):
names = ["mt", "mt-mt"]
past = "{0} ilu"
future = "fi {0}"
and_word = "u"
timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]] = {
"now": "issa",
"second": "sekonda",
"seconds": "{0} sekondi",
"minute": "minuta",
"minutes": "{0} minuti",
"hour": "siegħa",
"hours": {"dual": "{0} sagħtejn", "plural": "{0} sigħat"},
"day": "jum",
"days": {"dual": "{0} jumejn", "plural": "{0} ijiem"},
"week": "ġimgħa",
"weeks": {"dual": "{0} ġimagħtejn", "plural": "{0} ġimgħat"},
"month": "xahar",
"months": {"dual": "{0} xahrejn", "plural": "{0} xhur"},
"year": "sena",
"years": {"dual": "{0} sentejn", "plural": "{0} snin"},
}
month_names = [
"",
"Jannar",
"Frar",
"Marzu",
"April",
"Mejju",
"Ġunju",
"Lulju",
"Awwissu",
"Settembru",
"Ottubru",
"Novembru",
"Diċembru",
]
month_abbreviations = [
"",
"Jan",
"Fr",
"Mar",
"Apr",
"Mejju",
"Ġun",
"Lul",
"Aw",
"Sett",
"Ott",
"Nov",
"Diċ",
]
day_names = [
"",
"It-Tnejn",
"It-Tlieta",
"L-Erbgħa",
"Il-Ħamis",
"Il-Ġimgħa",
"Is-Sibt",
"Il-Ħadd",
]
day_abbreviations = [
"",
"T",
"TL",
"E",
"Ħ",
"Ġ",
"S",
"Ħ",
]
def _format_timeframe(self, timeframe: TimeFrameLiteral, delta: int) -> str:
form = self.timeframes[timeframe]
delta = abs(delta)
if isinstance(form, Mapping):
if delta == 2:
form = form["dual"]
else:
form = form["plural"]
return form.format(delta)
class SamiLocale(Locale):
names = ["se", "se-fi", "se-no", "se-se"]
past = "{0} dassái"
future = "{0} " # NOTE: couldn't find preposition for Sami here, none needed?
timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]] = {
"now": "dál",
"second": "sekunda",
"seconds": "{0} sekundda",
"minute": "minuhta",
"minutes": "{0} minuhta",
"hour": "diimmu",
"hours": "{0} diimmu",
"day": "beaivvi",
"days": "{0} beaivvi",
"week": "vahku",
"weeks": "{0} vahku",
"month": "mánu",
"months": "{0} mánu",
"year": "jagi",
"years": "{0} jagi",
}
month_names = [
"",
"Ođđajagimánnu",
"Guovvamánnu",
"Njukčamánnu",
"Cuoŋománnu",
"Miessemánnu",
"Geassemánnu",
"Suoidnemánnu",
"Borgemánnu",
"Čakčamánnu",
"Golggotmánnu",
"Skábmamánnu",
"Juovlamánnu",
]
month_abbreviations = [
"",
"Ođđajagimánnu",
"Guovvamánnu",
"Njukčamánnu",
"Cuoŋománnu",
"Miessemánnu",
"Geassemánnu",
"Suoidnemánnu",
"Borgemánnu",
"Čakčamánnu",
"Golggotmánnu",
"Skábmamánnu",
"Juovlamánnu",
]
day_names = [
"",
"Mánnodat",
"Disdat",
"Gaskavahkku",
"Duorastat",
"Bearjadat",
"Lávvordat",
"Sotnabeaivi",
]
day_abbreviations = [
"",
"Mánnodat",
"Disdat",
"Gaskavahkku",
"Duorastat",
"Bearjadat",
"Lávvordat",
"Sotnabeaivi",
]
class OdiaLocale(Locale):
names = ["or", "or-in"]
past = "{0} ପୂର୍ବେ"
future = "{0} ପରେ"
timeframes = {
"now": "ବର୍ତ୍ତମାନ",
"second": "ଏକ ସେକେଣ୍ଡ",
"seconds": "{0} ସେକେଣ୍ଡ",
"minute": "ଏକ ମିନଟ",
"minutes": "{0} ମିନଟ",
"hour": "ଏକ ଘଣ୍ଟା",
"hours": "{0} ଘଣ୍ଟା",
"day": "ଏକ ଦିନ",
"days": "{0} ଦିନ",
"month": "ଏକ ମାସ",
"months": "{0} ମାସ ",
"year": "ଏକ ବର୍ଷ",
"years": "{0} ବର୍ଷ",
}
meridians = {"am": "ପୂର୍ବାହ୍ନ", "pm": "ଅପରାହ୍ନ", "AM": "ପୂର୍ବାହ୍ନ", "PM": "ଅପରାହ୍ନ"}
month_names = [
"",
"ଜାନୁଆରୀ",
"ଫେବୃଆରୀ",
"ମାର୍ଚ୍ଚ୍",
"ଅପ୍ରେଲ",
"ମଇ",
"ଜୁନ୍",
"ଜୁଲାଇ",
"ଅଗଷ୍ଟ",
"ସେପ୍ଟେମ୍ବର",
"ଅକ୍ଟୋବର୍",
"ନଭେମ୍ବର୍",
"ଡିସେମ୍ବର୍",
]
month_abbreviations = [
"",
"ଜାନୁ",
"ଫେବୃ",
"ମାର୍ଚ୍ଚ୍",
"ଅପ୍ରେ",
"ମଇ",
"ଜୁନ୍",
"ଜୁଲା",
"ଅଗ",
"ସେପ୍ଟେ",
"ଅକ୍ଟୋ",
"ନଭେ",
"ଡିସେ",
]
day_names = [
"",
"ସୋମବାର",
"ମଙ୍ଗଳବାର",
"ବୁଧବାର",
"ଗୁରୁବାର",
"ଶୁକ୍ରବାର",
"ଶନିବାର",
"ରବିବାର",
]
day_abbreviations = [
"",
"ସୋମ",
"ମଙ୍ଗଳ",
"ବୁଧ",
"ଗୁରୁ",
"ଶୁକ୍ର",
"ଶନି",
"ରବି",
]
def _ordinal_number(self, n: int) -> str:
if n > 10 or n == 0:
return f"{n}ତମ"
if n in [1, 5, 7, 8, 9, 10]:
return f"{n}ମ"
if n in [2, 3]:
return f"{n}ୟ"
if n == 4:
return f"{n}ର୍ଥ"
if n == 6:
return f"{n}ଷ୍ଠ"
return ""
class SerbianLocale(Locale):
names = ["sr", "sr-rs", "sr-sp"]
past = "pre {0}"
future = "za {0}"
and_word = "i"
timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]] = {
"now": "sada",
"second": "sekundu",
"seconds": {"double": "{0} sekunde", "higher": "{0} sekundi"},
"minute": "minutu",
"minutes": {"double": "{0} minute", "higher": "{0} minuta"},
"hour": "sat",
"hours": {"double": "{0} sata", "higher": "{0} sati"},
"day": "dan",
"days": {"double": "{0} dana", "higher": "{0} dana"},
"week": "nedelju",
"weeks": {"double": "{0} nedelje", "higher": "{0} nedelja"},
"month": "mesec",
"months": {"double": "{0} meseca", "higher": "{0} meseci"},
"year": "godinu",
"years": {"double": "{0} godine", "higher": "{0} godina"},
}
month_names = [
"",
"januar", # јануар
"februar", # фебруар
"mart", # март
"april", # април
"maj", # мај
"jun", # јун
"jul", # јул
"avgust", # август
"septembar", # септембар
"oktobar", # октобар
"novembar", # новембар
"decembar", # децембар
]
month_abbreviations = [
"",
"jan",
"feb",
"mar",
"apr",
"maj",
"jun",
"jul",
"avg",
"sep",
"okt",
"nov",
"dec",
]
day_names = [
"",
"ponedeljak", # понедељак
"utorak", # уторак
"sreda", # среда
"četvrtak", # четвртак
"petak", # петак
"subota", # субота
"nedelja", # недеља
]
day_abbreviations = [
"",
"po", # по
"ut", # ут
"sr", # ср
"če", # че
"pe", # пе
"su", # су
"ne", # не
]
def _format_timeframe(self, timeframe: TimeFrameLiteral, delta: int) -> str:
form = self.timeframes[timeframe]
delta = abs(delta)
if isinstance(form, Mapping):
if 1 < delta <= 4:
form = form["double"]
else:
form = form["higher"]
return form.format(delta)
class LuxembourgishLocale(Locale):
names = ["lb", "lb-lu"]
past = "virun {0}"
future = "an {0}"
and_word = "an"
timeframes = {
"now": "just elo",
"second": "enger Sekonn",
"seconds": "{0} Sekonnen",
"minute": "enger Minutt",
"minutes": "{0} Minutten",
"hour": "enger Stonn",
"hours": "{0} Stonnen",
"day": "engem Dag",
"days": "{0} Deeg",
"week": "enger Woch",
"weeks": "{0} Wochen",
"month": "engem Mount",
"months": "{0} Méint",
"year": "engem Joer",
"years": "{0} Jahren",
}
timeframes_only_distance = timeframes.copy()
timeframes_only_distance["second"] = "eng Sekonn"
timeframes_only_distance["minute"] = "eng Minutt"
timeframes_only_distance["hour"] = "eng Stonn"
timeframes_only_distance["day"] = "een Dag"
timeframes_only_distance["days"] = "{0} Deeg"
timeframes_only_distance["week"] = "eng Woch"
timeframes_only_distance["month"] = "ee Mount"
timeframes_only_distance["months"] = "{0} Méint"
timeframes_only_distance["year"] = "ee Joer"
timeframes_only_distance["years"] = "{0} Joer"
month_names = [
"",
"Januar",
"Februar",
"Mäerz",
"Abrëll",
"Mee",
"Juni",
"Juli",
"August",
"September",
"Oktouber",
"November",
"Dezember",
]
month_abbreviations = [
"",
"Jan",
"Feb",
"Mäe",
"Abr",
"Mee",
"Jun",
"Jul",
"Aug",
"Sep",
"Okt",
"Nov",
"Dez",
]
day_names = [
"",
"Méindeg",
"Dënschdeg",
"Mëttwoch",
"Donneschdeg",
"Freideg",
"Samschdeg",
"Sonndeg",
]
day_abbreviations = ["", "Méi", "Dën", "Mët", "Don", "Fre", "Sam", "Son"]
def _ordinal_number(self, n: int) -> str:
return f"{n}."
def describe(
self,
timeframe: TimeFrameLiteral,
delta: Union[int, float] = 0,
only_distance: bool = False,
) -> str:
if not only_distance:
return super().describe(timeframe, delta, only_distance)
# Luxembourgish uses a different case without 'in' or 'ago'
humanized = self.timeframes_only_distance[timeframe].format(trunc(abs(delta)))
return humanized
class ZuluLocale(Locale):
names = ["zu", "zu-za"]
past = "{0} edlule"
future = "{0} "
and_word = "futhi"
timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[Mapping[str, str], str]]] = {
"now": "manje",
"second": {"past": "umzuzwana", "future": "ngomzuzwana"},
"seconds": {"past": "{0} imizuzwana", "future": "{0} ngemizuzwana"},
"minute": {"past": "umzuzu", "future": "ngomzuzu"},
"minutes": {"past": "{0} imizuzu", "future": "{0} ngemizuzu"},
"hour": {"past": "ihora", "future": "ngehora"},
"hours": {"past": "{0} amahora", "future": "{0} emahoreni"},
"day": {"past": "usuku", "future": "ngosuku"},
"days": {"past": "{0} izinsuku", "future": "{0} ezinsukwini"},
"week": {"past": "isonto", "future": "ngesonto"},
"weeks": {"past": "{0} amasonto", "future": "{0} emasontweni"},
"month": {"past": "inyanga", "future": "ngenyanga"},
"months": {"past": "{0} izinyanga", "future": "{0} ezinyangeni"},
"year": {"past": "unyaka", "future": "ngonyak"},
"years": {"past": "{0} iminyaka", "future": "{0} eminyakeni"},
}
def _format_timeframe(self, timeframe: TimeFrameLiteral, delta: int) -> str:
"""Zulu aware time frame format function, takes into account
the differences between past and future forms."""
abs_delta = abs(delta)
form = self.timeframes[timeframe]
if isinstance(form, str):
return form.format(abs_delta)
if delta > 0:
key = "future"
else:
key = "past"
form = form[key]
return form.format(abs_delta)
month_names = [
"",
"uMasingane",
"uNhlolanja",
"uNdasa",
"UMbasa",
"UNhlaba",
"UNhlangulana",
"uNtulikazi",
"UNcwaba",
"uMandulo",
"uMfumfu",
"uLwezi",
"uZibandlela",
]
month_abbreviations = [
"",
"uMasingane",
"uNhlolanja",
"uNdasa",
"UMbasa",
"UNhlaba",
"UNhlangulana",
"uNtulikazi",
"UNcwaba",
"uMandulo",
"uMfumfu",
"uLwezi",
"uZibandlela",
]
day_names = [
"",
"uMsombuluko",
"uLwesibili",
"uLwesithathu",
"uLwesine",
"uLwesihlanu",
"uMgqibelo",
"iSonto",
]
day_abbreviations = [
"",
"uMsombuluko",
"uLwesibili",
"uLwesithathu",
"uLwesine",
"uLwesihlanu",
"uMgqibelo",
"iSonto",
]
class TamilLocale(Locale):
names = ["ta", "ta-in", "ta-lk"]
past = "{0} நேரத்திற்கு முன்பு"
future = "இல் {0}"
timeframes = {
"now": "இப்போது",
"second": "ஒரு இரண்டாவது",
"seconds": "{0} விநாடிகள்",
"minute": "ஒரு நிமிடம்",
"minutes": "{0} நிமிடங்கள்",
"hour": "ஒரு மணி",
"hours": "{0} மணிநேரம்",
"day": "ஒரு நாள்",
"days": "{0} நாட்கள்",
"week": "ஒரு வாரம்",
"weeks": "{0} வாரங்கள்",
"month": "ஒரு மாதம்",
"months": "{0} மாதங்கள்",
"year": "ஒரு ஆண்டு",
"years": "{0} ஆண்டுகள்",
}
month_names = [
"",
"சித்திரை",
"வைகாசி",
"ஆனி",
"ஆடி",
"ஆவணி",
"புரட்டாசி",
"ஐப்பசி",
"கார்த்திகை",
"மார்கழி",
"தை",
"மாசி",
"பங்குனி",
]
month_abbreviations = [
"",
"ஜன",
"பிப்",
"மார்",
"ஏப்",
"மே",
"ஜூன்",
"ஜூலை",
"ஆக",
"செப்",
"அக்",
"நவ",
"டிச",
]
day_names = [
"",
"திங்கட்கிழமை",
"செவ்வாய்க்கிழமை",
"புதன்கிழமை",
"வியாழக்கிழமை",
"வெள்ளிக்கிழமை",
"சனிக்கிழமை",
"ஞாயிற்றுக்கிழமை",
]
day_abbreviations = [
"",
"திங்கட்",
"செவ்வாய்",
"புதன்",
"வியாழன்",
"வெள்ளி",
"சனி",
"ஞாயிறு",
]
def _ordinal_number(self, n: int) -> str:
if n == 1:
return f"{n}வது"
elif n >= 0:
return f"{n}ஆம்"
else:
return ""
class AlbanianLocale(Locale):
names = ["sq", "sq-al"]
past = "{0} më parë"
future = "në {0}"
and_word = "dhe"
timeframes = {
"now": "tani",
"second": "sekondë",
"seconds": "{0} sekonda",
"minute": "minutë",
"minutes": "{0} minuta",
"hour": "orë",
"hours": "{0} orë",
"day": "ditë",
"days": "{0} ditë",
"week": "javë",
"weeks": "{0} javë",
"month": "muaj",
"months": "{0} muaj",
"year": "vit",
"years": "{0} vjet",
}
month_names = [
"",
"janar",
"shkurt",
"mars",
"prill",
"maj",
"qershor",
"korrik",
"gusht",
"shtator",
"tetor",
"nëntor",
"dhjetor",
]
month_abbreviations = [
"",
"jan",
"shk",
"mar",
"pri",
"maj",
"qer",
"korr",
"gush",
"sht",
"tet",
"nën",
"dhj",
]
day_names = [
"",
"e hënë",
"e martë",
"e mërkurë",
"e enjte",
"e premte",
"e shtunë",
"e diel",
]
day_abbreviations = [
"",
"hën",
"mar",
"mër",
"enj",
"pre",
"sht",
"die",
]
class SinhalaLocale(Locale):
names = ["si", "si-lk"]
past = "{0}ට පෙර"
future = "{0}"
and_word = "සහ"
timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[Mapping[str, str], str]]] = {
"now": "දැන්",
"second": {
"past": "තත්පරයක",
"future": "තත්පරයකින්",
}, # ක් is the article
"seconds": {
"past": "තත්පර {0} ක",
"future": "තත්පර {0} කින්",
},
"minute": {
"past": "විනාඩියක",
"future": "විනාඩියකින්",
},
"minutes": {
"past": "විනාඩි {0} ක",
"future": "මිනිත්තු {0} කින්",
},
"hour": {"past": "පැයක", "future": "පැයකින්"},
"hours": {
"past": "පැය {0} ක",
"future": "පැය {0} කින්",
},
"day": {"past": "දිනක", "future": "දිනකට"},
"days": {
"past": "දින {0} ක",
"future": "දින {0} කින්",
},
"week": {"past": "සතියක", "future": "සතියකින්"},
"weeks": {
"past": "සති {0} ක",
"future": "සති {0} කින්",
},
"month": {"past": "මාසයක", "future": "එය මාසය තුළ"},
"months": {
"past": "මාස {0} ක",
"future": "මාස {0} කින්",
},
"year": {"past": "වසරක", "future": "වසරක් තුළ"},
"years": {
"past": "අවුරුදු {0} ක",
"future": "අවුරුදු {0} තුළ",
},
}
# Sinhala: the general format to describe timeframe is different from past and future,
# so we do not copy the original timeframes dictionary
timeframes_only_distance = dict()
timeframes_only_distance["second"] = "තත්පරයක්"
timeframes_only_distance["seconds"] = "තත්පර {0}"
timeframes_only_distance["minute"] = "මිනිත්තුවක්"
timeframes_only_distance["minutes"] = "විනාඩි {0}"
timeframes_only_distance["hour"] = "පැයක්"
timeframes_only_distance["hours"] = "පැය {0}"
timeframes_only_distance["day"] = "දවසක්"
timeframes_only_distance["days"] = "දවස් {0}"
timeframes_only_distance["week"] = "සතියක්"
timeframes_only_distance["weeks"] = "සති {0}"
timeframes_only_distance["month"] = "මාසයක්"
timeframes_only_distance["months"] = "මාස {0}"
timeframes_only_distance["year"] = "අවුරුද්දක්"
timeframes_only_distance["years"] = "අවුරුදු {0}"
def _format_timeframe(self, timeframe: TimeFrameLiteral, delta: int) -> str:
"""
Sinhala awares time frame format function, takes into account
the differences between general, past, and future forms (three different suffixes).
"""
abs_delta = abs(delta)
form = self.timeframes[timeframe]
if isinstance(form, str):
return form.format(abs_delta)
if delta > 0:
key = "future"
else:
key = "past"
form = form[key]
return form.format(abs_delta)
def describe(
self,
timeframe: TimeFrameLiteral,
delta: Union[float, int] = 1, # key is always future when only_distance=False
only_distance: bool = False,
) -> str:
"""Describes a delta within a timeframe in plain language.
:param timeframe: a string representing a timeframe.
:param delta: a quantity representing a delta in a timeframe.
:param only_distance: return only distance eg: "11 seconds" without "in" or "ago" keywords
"""
if not only_distance:
return super().describe(timeframe, delta, only_distance)
# Sinhala uses a different case without 'in' or 'ago'
humanized = self.timeframes_only_distance[timeframe].format(trunc(abs(delta)))
return humanized
month_names = [
"",
"ජනවාරි",
"පෙබරවාරි",
"මාර්තු",
"අප්රේල්",
"මැයි",
"ජූනි",
"ජූලි",
"අගෝස්තු",
"සැප්තැම්බර්",
"ඔක්තෝබර්",
"නොවැම්බර්",
"දෙසැම්බර්",
]
month_abbreviations = [
"",
"ජන",
"පෙබ",
"මාර්",
"අප්රේ",
"මැයි",
"ජුනි",
"ජූලි",
"අගෝ",
"සැප්",
"ඔක්",
"නොවැ",
"දෙසැ",
]
day_names = [
"",
"සදුදා",
"අඟහරැවදා",
"බදාදා",
"බ්රහස්පතින්දා",
"සිකුරාදා",
"සෙනසුරාදා",
"ඉරිදා",
]
day_abbreviations = [
"",
"සදුද",
"බදා",
"බදා",
"සිකු",
"සෙන",
"අ",
"ඉරිදා",
]
class UrduLocale(Locale):
names = ["ur", "ur-pk"]
past = "پہلے {0}"
future = "میں {0}"
and_word = "اور"
timeframes = {
"now": "ابھی",
"second": "ایک سیکنڈ",
"seconds": "{0} سیکنڈ",
"minute": "ایک منٹ",
"minutes": "{0} منٹ",
"hour": "ایک گھنٹے",
"hours": "{0} گھنٹے",
"day": "ایک دن",
"days": "{0} دن",
"week": "ایک ہفتے",
"weeks": "{0} ہفتے",
"month": "ایک مہینہ",
"months": "{0} ماہ",
"year": "ایک سال",
"years": "{0} سال",
}
month_names = [
"",
"جنوری",
"فروری",
"مارچ",
"اپریل",
"مئی",
"جون",
"جولائی",
"اگست",
"ستمبر",
"اکتوبر",
"نومبر",
"دسمبر",
]
month_abbreviations = [
"",
"جنوری",
"فروری",
"مارچ",
"اپریل",
"مئی",
"جون",
"جولائی",
"اگست",
"ستمبر",
"اکتوبر",
"نومبر",
"دسمبر",
]
day_names = [
"",
"سوموار",
"منگل",
"بدھ",
"جمعرات",
"جمعہ",
"ہفتہ",
"اتوار",
]
day_abbreviations = [
"",
"سوموار",
"منگل",
"بدھ",
"جمعرات",
"جمعہ",
"ہفتہ",
"اتوار",
]
python-arrow-1.2.1/arrow/parser.py 0000664 0000000 0000000 00000062170 14151001236 0017163 0 ustar 00root root 0000000 0000000 """Provides the :class:`Arrow ` class, a better way to parse datetime strings."""
import re
import sys
from datetime import datetime, timedelta
from datetime import tzinfo as dt_tzinfo
from functools import lru_cache
from typing import (
Any,
ClassVar,
Dict,
Iterable,
List,
Match,
Optional,
Pattern,
SupportsFloat,
SupportsInt,
Tuple,
Union,
cast,
overload,
)
from dateutil import tz
from arrow import locales
from arrow.constants import DEFAULT_LOCALE
from arrow.util import next_weekday, normalize_timestamp
if sys.version_info < (3, 8): # pragma: no cover
from typing_extensions import Literal, TypedDict
else:
from typing import Literal, TypedDict # 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
_WEEKDATE_ELEMENT = Union[str, bytes, SupportsInt, bytearray]
_FORMAT_TYPE = Literal[
"YYYY",
"YY",
"MM",
"M",
"DDDD",
"DDD",
"DD",
"D",
"HH",
"H",
"hh",
"h",
"mm",
"m",
"ss",
"s",
"X",
"x",
"ZZZ",
"ZZ",
"Z",
"S",
"W",
"MMMM",
"MMM",
"Do",
"dddd",
"ddd",
"d",
"a",
"A",
]
class _Parts(TypedDict, total=False):
year: int
month: int
day_of_year: int
day: int
hour: int
minute: int
second: int
microsecond: int
timestamp: float
expanded_timestamp: int
tzinfo: dt_tzinfo
am_pm: Literal["am", "pm"]
day_of_week: int
weekdate: Tuple[_WEEKDATE_ELEMENT, _WEEKDATE_ELEMENT, Optional[_WEEKDATE_ELEMENT]]
class DateTimeParser:
_FORMAT_RE: ClassVar[Pattern[str]] = 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: ClassVar[Pattern[str]] = re.compile(r"\[[^\[\]]*\]")
_ONE_OR_TWO_DIGIT_RE: ClassVar[Pattern[str]] = re.compile(r"\d{1,2}")
_ONE_OR_TWO_OR_THREE_DIGIT_RE: ClassVar[Pattern[str]] = re.compile(r"\d{1,3}")
_ONE_OR_MORE_DIGIT_RE: ClassVar[Pattern[str]] = re.compile(r"\d+")
_TWO_DIGIT_RE: ClassVar[Pattern[str]] = re.compile(r"\d{2}")
_THREE_DIGIT_RE: ClassVar[Pattern[str]] = re.compile(r"\d{3}")
_FOUR_DIGIT_RE: ClassVar[Pattern[str]] = re.compile(r"\d{4}")
_TZ_Z_RE: ClassVar[Pattern[str]] = re.compile(r"([\+\-])(\d{2})(?:(\d{2}))?|Z")
_TZ_ZZ_RE: ClassVar[Pattern[str]] = re.compile(r"([\+\-])(\d{2})(?:\:(\d{2}))?|Z")
_TZ_NAME_RE: ClassVar[Pattern[str]] = 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: ClassVar[Pattern[str]] = re.compile(r"^\-?\d+\.?\d+$")
_TIMESTAMP_EXPANDED_RE: ClassVar[Pattern[str]] = re.compile(r"^\-?\d+$")
_TIME_RE: ClassVar[Pattern[str]] = re.compile(
r"^(\d{2})(?:\:?(\d{2}))?(?:\:?(\d{2}))?(?:([\.\,])(\d+))?$"
)
_WEEK_DATE_RE: ClassVar[Pattern[str]] = re.compile(
r"(?P\d{4})[\-]?W(?P\d{2})[\-]?(?P\d)?"
)
_BASE_INPUT_RE_MAP: ClassVar[Dict[_FORMAT_TYPE, Pattern[str]]] = {
"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: ClassVar[List[str]] = ["-", "/", "."]
locale: locales.Locale
_input_re_map: Dict[_FORMAT_TYPE, Pattern[str]]
def __init__(self, locale: str = DEFAULT_LOCALE, cache_size: int = 0) -> None:
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)( # type: ignore
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: str, normalize_whitespace: bool = False
) -> datetime:
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(
f"Expected an ISO 8601-like string, but was given {datetime_string!r}. "
"Try passing in a format string to resolve this."
)
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: Optional[Match[str]] = 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 = f"HH{time_sep}mm"
else:
time_string = "HH"
if has_space_divider:
formats = [f"{f} {time_string}" for f in formats]
else:
formats = [f"{f}T{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 = [f"{f}{tz_format}" for f in formats]
return self._parse_multiformat(datetime_string, formats)
def parse(
self,
datetime_string: str,
fmt: Union[List[str], str],
normalize_whitespace: bool = False,
) -> datetime:
if normalize_whitespace:
datetime_string = re.sub(r"\s+", " ", datetime_string)
if isinstance(fmt, list):
return self._parse_multiformat(datetime_string, fmt)
try:
fmt_tokens: List[_FORMAT_TYPE]
fmt_pattern_re: Pattern[str]
fmt_tokens, fmt_pattern_re = self._generate_pattern_re(fmt)
except re.error as e:
raise ParserMatchError(
f"Failed to generate regular expression pattern: {e}."
)
match = fmt_pattern_re.search(datetime_string)
if match is None:
raise ParserMatchError(
f"Failed to match {fmt!r} when parsing {datetime_string!r}."
)
parts: _Parts = {}
for token in fmt_tokens:
value: Union[Tuple[str, str, str], str]
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)
if value is None:
raise ParserMatchError(
f"Unable to find a match group for the specified token {token!r}."
)
self._parse_token(token, value, parts) # type: ignore
return self._build_datetime(parts)
def _generate_pattern_re(self, fmt: str) -> Tuple[List[_FORMAT_TYPE], Pattern[str]]:
# 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\d{4})-(?P\d{2})-(?P\d{2})'
tokens: List[_FORMAT_TYPE] = []
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: _FORMAT_TYPE = cast(_FORMAT_TYPE, m.group(0))
try:
input_re = self._input_re_map[token]
except KeyError:
raise ParserError(f"Unrecognized token {token!r}.")
input_pattern = f"(?P<{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])" # 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)
@overload
def _parse_token(
self,
token: Literal[
"YYYY",
"YY",
"MM",
"M",
"DDDD",
"DDD",
"DD",
"D",
"Do",
"HH",
"hh",
"h",
"H",
"mm",
"m",
"ss",
"s",
"x",
],
value: Union[str, bytes, SupportsInt, bytearray],
parts: _Parts,
) -> None:
... # pragma: no cover
@overload
def _parse_token(
self,
token: Literal["X"],
value: Union[str, bytes, SupportsFloat, bytearray],
parts: _Parts,
) -> None:
... # pragma: no cover
@overload
def _parse_token(
self,
token: Literal["MMMM", "MMM", "dddd", "ddd", "S"],
value: Union[str, bytes, bytearray],
parts: _Parts,
) -> None:
... # pragma: no cover
@overload
def _parse_token(
self,
token: Literal["a", "A", "ZZZ", "ZZ", "Z"],
value: Union[str, bytes],
parts: _Parts,
) -> None:
... # pragma: no cover
@overload
def _parse_token(
self,
token: Literal["W"],
value: Tuple[_WEEKDATE_ELEMENT, _WEEKDATE_ELEMENT, Optional[_WEEKDATE_ELEMENT]],
parts: _Parts,
) -> None:
... # pragma: no cover
def _parse_token(
self,
token: Any,
value: Any,
parts: _Parts,
) -> None:
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"]:
# FIXME: month_number() is nullable
parts["month"] = self.locale.month_number(value.lower()) # type: ignore
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, "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"
if "hour" in parts and not 0 <= parts["hour"] <= 12:
raise ParserMatchError(
f"Hour token value must be between 0 and 12 inclusive for token {token!r}."
)
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: _Parts) -> datetime:
weekdate = parts.get("weekdate")
if weekdate is not None:
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
date_string = f"{year}-{week}-{_day}"
# tokens for ISO 8601 weekdates
dt = datetime.strptime(date_string, "%G-%V-%u")
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 = f"{_year}-{day_of_year}"
try:
dt = datetime.strptime(date_string, "%Y-%j")
except ValueError:
raise ParserError(
f"The provided day of year {day_of_year!r} is invalid."
)
parts["year"] = dt.year
parts["month"] = dt.month
parts["day"] = dt.day
day_of_week: Optional[int] = 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: str, formats: Iterable[str]) -> datetime:
_datetime: Optional[datetime] = None
for fmt in formats:
try:
_datetime = self.parse(string, fmt)
break
except ParserMatchError:
pass
if _datetime is None:
supported_formats = ", ".join(formats)
raise ParserError(
f"Could not match input {string!r} to any of the following formats: {supported_formats}."
)
return _datetime
# generates a capture group of choices separated by an OR operator
@staticmethod
def _generate_choice_re(
choices: Iterable[str], flags: Union[int, re.RegexFlag] = 0
) -> Pattern[str]:
return re.compile(r"({})".format("|".join(choices)), flags=flags)
class TzinfoParser:
_TZINFO_RE: ClassVar[Pattern[str]] = re.compile(
r"^([\+\-])?(\d{2})(?:\:?(\d{2}))?$"
)
@classmethod
def parse(cls, tzinfo_string: str) -> dt_tzinfo:
tzinfo: Optional[dt_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: Optional[str]
hours: str
minutes: Union[str, int, None]
sign, hours, minutes = iso_match.groups()
seconds = int(hours) * 3600 + int(minutes or 0) * 60
if sign == "-":
seconds *= -1
tzinfo = tz.tzoffset(None, seconds)
else:
tzinfo = tz.gettz(tzinfo_string)
if tzinfo is None:
raise ParserError(f"Could not parse timezone expression {tzinfo_string!r}.")
return tzinfo
python-arrow-1.2.1/arrow/py.typed 0000664 0000000 0000000 00000000000 14151001236 0016774 0 ustar 00root root 0000000 0000000 python-arrow-1.2.1/arrow/util.py 0000664 0000000 0000000 00000007137 14151001236 0016646 0 ustar 00root root 0000000 0000000 """Helpful functions used internally within arrow."""
import datetime
from typing import Any, Optional, cast
from dateutil.rrule import WEEKLY, rrule
from arrow.constants import (
MAX_ORDINAL,
MAX_TIMESTAMP,
MAX_TIMESTAMP_MS,
MAX_TIMESTAMP_US,
MIN_ORDINAL,
)
def next_weekday(
start_date: Optional[datetime.date], weekday: int
) -> datetime.datetime:
"""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 cast(
datetime.datetime,
rrule(freq=WEEKLY, dtstart=start_date, byweekday=weekday, count=1)[0],
)
def is_timestamp(value: Any) -> bool:
"""Check if value is a valid timestamp."""
if isinstance(value, bool):
return False
if not isinstance(value, (int, float, str)):
return False
try:
float(value)
return True
except ValueError:
return False
def validate_ordinal(value: Any) -> None:
"""Raise an exception if value is an invalid Gregorian ordinal.
:param value: the input to be checked
"""
if isinstance(value, bool) or not isinstance(value, int):
raise TypeError(f"Ordinal must be an integer (got type {type(value)}).")
if not (MIN_ORDINAL <= value <= MAX_ORDINAL):
raise ValueError(f"Ordinal {value} is out of range.")
def normalize_timestamp(timestamp: float) -> float:
"""Normalize millisecond and microsecond timestamps into normal timestamps."""
if timestamp > MAX_TIMESTAMP:
if timestamp < MAX_TIMESTAMP_MS:
timestamp /= 1000
elif timestamp < MAX_TIMESTAMP_US:
timestamp /= 1_000_000
else:
raise ValueError(f"The specified timestamp {timestamp!r} is too large.")
return timestamp
# Credit to https://stackoverflow.com/a/1700069
def iso_to_gregorian(iso_year: int, iso_week: int, iso_day: int) -> datetime.date:
"""Converts an ISO week date into a datetime object.
:param iso_year: the year
:param iso_week: the week number, each year has either 52 or 53 weeks
:param iso_day: the day numbered 1 through 7, beginning with Monday
"""
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: str) -> None:
if bounds != "()" and bounds != "(]" and bounds != "[)" and bounds != "[]":
raise ValueError(
"Invalid bounds. Please select between '()', '(]', '[)', or '[]'."
)
__all__ = ["next_weekday", "is_timestamp", "validate_ordinal", "iso_to_gregorian"]
python-arrow-1.2.1/docs/ 0000775 0000000 0000000 00000000000 14151001236 0015105 5 ustar 00root root 0000000 0000000 python-arrow-1.2.1/docs/Makefile 0000664 0000000 0000000 00000001172 14151001236 0016546 0 ustar 00root root 0000000 0000000 # 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)
python-arrow-1.2.1/docs/conf.py 0000664 0000000 0000000 00000002773 14151001236 0016415 0 ustar 00root root 0000000 0000000 # mypy: ignore-errors
# -- Path setup --------------------------------------------------------------
import os
import sys
sys.path.insert(0, os.path.abspath(".."))
about = {}
with open("../arrow/_version.py", encoding="utf-8") as f:
exec(f.read(), about)
# -- Project information -----------------------------------------------------
project = "Arrow 🏹"
copyright = "2021, Chris Smith"
author = "Chris Smith"
release = about["__version__"]
# -- General configuration ---------------------------------------------------
extensions = ["sphinx.ext.autodoc", "sphinx_autodoc_typehints"]
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"]
}
python-arrow-1.2.1/docs/index.rst 0000664 0000000 0000000 00000047527 14151001236 0016765 0 ustar 00root root 0000000 0000000 Arrow: Better dates & times for Python
======================================
Release v\ |release| (`Installation`_) (`Changelog `_)
.. 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.now()