pax_global_header00006660000000000000000000000064145374103350014517gustar00rootroot0000000000000052 comment=0fcd10217af0469b3edda072f2b152d5273f3d58 pendulum-3.0.0/000077500000000000000000000000001453741033500133505ustar00rootroot00000000000000pendulum-3.0.0/.github/000077500000000000000000000000001453741033500147105ustar00rootroot00000000000000pendulum-3.0.0/.github/FUNDING.yml000066400000000000000000000000241453741033500165210ustar00rootroot00000000000000github: [sdispater] pendulum-3.0.0/.github/ISSUE_TEMPLATE/000077500000000000000000000000001453741033500170735ustar00rootroot00000000000000pendulum-3.0.0/.github/ISSUE_TEMPLATE/---bug-report.md000066400000000000000000000017771453741033500217260ustar00rootroot00000000000000--- name: "\U0001F41E Bug Report" about: Did you find a bug? title: '' labels: 'Bug' assignees: '' --- - [ ] I am on the [latest](https://github.com/sdispater/pendulum/releases/latest) Pendulum version. - [ ] I have searched the [issues](https://github.com/sdispater/pendulum/issues) of this repo and believe that this is not a duplicate. - **OS version and name**: - **Pendulum version**: ## Issue pendulum-3.0.0/.github/ISSUE_TEMPLATE/---documentation.md000066400000000000000000000014041453741033500224740ustar00rootroot00000000000000--- name: "\U0001F4DA Documentation" about: Did you find errors, problems, or anything unintelligible in the docs (https://pendulum.eustace.io/docs)? title: '' labels: 'Documentation' assignees: '' --- - [ ] I have searched the [issues](https://github.com/sdispater/pendulum/issues) of this repo and believe that this is not a duplicate. ## Issue pendulum-3.0.0/.github/ISSUE_TEMPLATE/---everything-else.md000066400000000000000000000015551453741033500227440ustar00rootroot00000000000000--- name: "\U0001F5C3 Everything Else" about: For questions and issues that do not fall in any of the other categories. This can include questions about Pendulum's roadmap. title: '' labels: '' assignees: '' --- - [ ] I have searched the [issues](https://github.com/sdispater/pendulum/issues) of this repo and believe that this is not a duplicate. - [ ] I have searched the [documentation](https://pendulum.eustace.io/docs/) and believe that my question is not covered. ## Issue pendulum-3.0.0/.github/ISSUE_TEMPLATE/---feature-request.md000066400000000000000000000014501453741033500227450ustar00rootroot00000000000000--- name: "\U0001F381 Feature Request" about: Do you have ideas for new features and improvements? title: '' labels: 'Feature' assignees: '' --- - [ ] I have searched the [issues](https://github.com/sdispater/pendulum/issues) of this repo and believe that this is not a duplicate. - [ ] I have searched the [documentation](https://pendulum.eustace.io/docs/) and believe that my question is not covered. ## Feature Request pendulum-3.0.0/.github/PULL_REQUEST_TEMPLATE.md000066400000000000000000000014161453741033500205130ustar00rootroot00000000000000## Pull Request Check List - [ ] Added **tests** for changed code. - [ ] Updated **documentation** for changed code. pendulum-3.0.0/.github/workflows/000077500000000000000000000000001453741033500167455ustar00rootroot00000000000000pendulum-3.0.0/.github/workflows/codspeed.yml000066400000000000000000000027071453741033500212640ustar00rootroot00000000000000name: codspeed on: push: branches: - "master" pull_request: # `workflow_dispatch` allows CodSpeed to trigger backtest # performance analysis in order to generate initial data. workflow_dispatch: jobs: benchmarks: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: actions/setup-python@v3 with: python-version: "3.9" - name: Get full Python version id: full-python-version run: | echo ::set-output name=version::$(python -c "import sys; print('-'.join(str(v) for v in sys.version_info))") - name: Install poetry run: | curl -fsS https://install.python-poetry.org | python - -y - name: Update PATH if: ${{ matrix.os != 'Windows' }} run: echo "$HOME/.local/bin" >> $GITHUB_PATH - name: Configure poetry run: poetry config virtualenvs.create false - name: Install dependencies run: poetry install --only test --only benchmark --only build -vvv --no-root - name: Install project run: poetry install --only test --only benchmark --only build -vvv --no-root - name: Install pendulum and check extensions run: | poetry run pip install -e . -vvv python -c 'import pendulum._pendulum' - name: Run benchmarks uses: CodSpeedHQ/action@v1 with: token: ${{ secrets.CODSPEED_TOKEN }} run: pytest tests/ --codspeed pendulum-3.0.0/.github/workflows/release.yml000066400000000000000000000101051453741033500211050ustar00rootroot00000000000000name: Release on: push: tags: - '*.*.*' workflow_dispatch: jobs: build: name: Build on ${{ matrix.platform || matrix.os }} (${{ matrix.target }} - ${{ matrix.manylinux || 'auto' }}) strategy: fail-fast: false matrix: os: [ubuntu, macos, windows] target: [x86_64, aarch64] manylinux: [auto] include: - os: ubuntu platform: linux - os: windows ls: dir interpreter: 3.7 3.8 3.9 3.10 3.11 3.12 pypy3.8 pypy3.9 pypy3.10 - os: windows ls: dir target: aarch64 interpreter: 3.11 3.12 - os: macos target: aarch64 interpreter: 3.7 3.8 3.9 3.10 3.11 3.12 pypy3.8 pypy3.9 pypy3.10 - os: ubuntu platform: linux target: aarch64 # mimalloc not supported on manylinux2014 cross-compile container extra-build-args: --no-default-features # musllinux - os: ubuntu platform: linux target: x86_64 manylinux: musllinux_1_1 - os: ubuntu platform: linux target: aarch64 manylinux: musllinux_1_1 - os: ubuntu platform: linux target: ppc64le interpreter: 3.7 3.8 3.9 3.10 3.11 3.12 # mimalloc not supported on manylinux2014 cross-compile container extra-build-args: --no-default-features - os: ubuntu platform: linux target: s390x interpreter: 3.7 3.8 3.9 3.10 3.11 3.12 # mimalloc not supported on manylinux2014 cross-compile container extra-build-args: --no-default-features runs-on: ${{ matrix.os }}-latest steps: - uses: actions/checkout@v4 - name: set up python uses: actions/setup-python@v4 with: python-version: '3.11' architecture: ${{ matrix.python-architecture || 'x64' }} - name: build wheels uses: PyO3/maturin-action@v1 with: target: ${{ matrix.target }} manylinux: ${{ matrix.manylinux || 'auto' }} container: ${{ matrix.container }} args: --release --out dist --interpreter ${{ matrix.interpreter || '3.7 3.8 3.9 3.10 3.11 3.12 pypy3.7 pypy3.8 pypy3.9 pypy3.10' }} ${{ matrix.extra-build-args }} rust-toolchain: stable docker-options: -e CI - run: ${{ matrix.ls || 'ls -lh' }} dist/ - uses: actions/upload-artifact@v3 with: name: dist path: dist build_sdist: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Build sdist uses: PyO3/maturin-action@v1 with: command: sdist args: --out dist - name: Upload sdist uses: actions/upload-artifact@v3 with: name: dist path: dist Release: needs: [ build, build_sdist ] if: success() && startsWith(github.ref, 'refs/tags/') runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v2 - name: Download artifacts uses: actions/download-artifact@v3 with: name: dist path: dist - name: Install Poetry run: | curl -fsS https://install.python-poetry.org | python - -y - name: Update PATH run: echo "$HOME/.local/bin" >> $GITHUB_PATH - name: Check distributions run: | ls -la dist - name: Check Version id: check-version run: | [[ "${GITHUB_REF#refs/tags/}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] \ || echo ::set-output name=prerelease::true - name: Create Release uses: ncipollo/release-action@v1 with: artifacts: "dist/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false prerelease: steps.check-version.outputs.prerelease == 'true' - name: Publish to PyPI env: POETRY_PYPI_TOKEN_PYPI: ${{ secrets.PYPI_TOKEN }} run: | poetry publish pendulum-3.0.0/.github/workflows/tests.yml000066400000000000000000000055441453741033500206420ustar00rootroot00000000000000name: Tests on: push: paths-ignore: - 'docs/**' branches: - master pull_request: paths-ignore: - 'docs/**' branches: - '**' jobs: Linting: name: Linting runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: actions/setup-python@v4 with: python-version: "3.11" - name: "Install pre-commit" run: pip install pre-commit - name: "Install Rust toolchain" run: rustup component add rustfmt clippy - run: pre-commit run --all-files Tests: name: ${{ matrix.os }} / ${{ matrix.python-version }} runs-on: ${{ matrix.os }}-latest strategy: matrix: os: [Ubuntu, MacOS, Windows] python-version: [3.8, 3.9, "3.10", "3.11", "3.12"] defaults: run: shell: bash steps: - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} allow-prereleases: true - name: Get full Python version id: full-python-version run: | echo ::set-output name=version::$(python -c "import sys; print('-'.join(str(v) for v in sys.version_info))") - name: Install poetry run: | curl -fsS https://install.python-poetry.org | python - --preview -y - name: Update PATH if: ${{ matrix.os != 'Windows' }} run: echo "$HOME/.local/bin" >> $GITHUB_PATH - name: Update Path for Windows if: ${{ matrix.os == 'Windows' }} run: echo "$APPDATA\Python\Scripts" >> $GITHUB_PATH - name: Configure poetry run: poetry config virtualenvs.in-project true - name: Set up cache uses: actions/cache@v3 id: cache with: path: .venv key: venv-${{ runner.os }}-${{ steps.full-python-version.outputs.version }}-${{ hashFiles('**/poetry.lock') }} - name: Ensure cache is healthy # MacOS does not come with `timeout` command out of the box if: steps.cache.outputs.cache-hit == 'true' && matrix.os != 'MacOS' run: timeout 10s poetry run pip --version || rm -rf .venv - name: Install runtime, testing, and typing dependencies run: poetry install --only main --only test --only typing --only build --no-root -vvv - name: Install project run: poetry run maturin develop - name: Run type checking run: poetry run mypy - name: Uninstall typing dependencies # This ensures pendulum runs without typing_extensions installed run: poetry install --only main --only test --only build --sync --no-root -vvv - name: Test Pure Python run: | PENDULUM_EXTENSIONS=0 poetry run pytest -q tests - name: Test run: | poetry run pytest -q tests pendulum-3.0.0/.gitignore000066400000000000000000000005371453741033500153450ustar00rootroot00000000000000*.pyc # Packages *.egg *.egg-info dist build _build .cache *.so # Installer logs pip-log.txt # Unit test / coverage reports .coverage .tox nosetests.xml .pytest_cache .DS_Store .idea/* .python-version /test.py /test_*.* /benchmark/* benchmark.py results.json profile.html /wheelhouse /docs/site/* setup.py # editor .vscode /target /rust/target pendulum-3.0.0/.pre-commit-config.yaml000066400000000000000000000012141453741033500176270ustar00rootroot00000000000000ci: autofix_prs: false repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.4.0 hooks: - id: trailing-whitespace exclude: ^tests/.*/fixtures/.* - id: end-of-file-fixer exclude: ^tests/.*/fixtures/.* - id: debug-statements - repo: https://github.com/psf/black rev: 23.7.0 hooks: - id: black - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.0.291 hooks: - id: ruff - repo: local hooks: - id: lint-rust name: Lint Rust entry: make lint-rust types: [rust] language: rust pass_filenames: false pendulum-3.0.0/CHANGELOG.md000066400000000000000000000227671453741033500151770ustar00rootroot00000000000000# Change Log ## [3.0.0] - 2023-12-16 ### Changed - Relaxed dependency constraints. [#760](https://github.com/sdispater/pendulum/pull/760) - The testing helpers are now optional and must be opted-in via the `test` extra. [#778](https://github.com/sdispater/pendulum/pull/778) ### Fixed - Removed remaining mentions of periods instead of intervals. [#757](https://github.com/sdispater/pendulum/pull/757) - Fixed the behavior of the `week_of_month` property for edge cases in January and December. [#774](https://github.com/sdispater/pendulum/pull/774) - Fixed the handling of the `fold` attribute when deep-copying a `DateTime` instance. [#776](https://github.com/sdispater/pendulum/pull/776) - Fixed errors where hours and days were not handled properly when adding durations. [#775](https://github.com/sdispater/pendulum/pull/775) - Fixed errors where hours and days were not handled properly when adding durations. [#775](https://github.com/sdispater/pendulum/pull/775) ## [3.0.0b1] - 2023-10-01 ### Added - Made `instance()` support all native types (date, time, datetime). [#732](https://github.com/sdispater/pendulum/pull/732) ### Changed - Dropped support for Python 3.7. [#734](https://github.com/sdispater/pendulum/pull/734) - Rewrote extensions in Rust. [#721](https://github.com/sdispater/pendulum/pull/721) - Made day of week convention more consistent across the codebase. [#731](https://github.com/sdispater/pendulum/pull/731) ### Fixed - Fixed datetime string representation to match the native library. [#733](https://github.com/sdispater/pendulum/pull/733) - Fixed issues on some system when retrieving the local timezone. [#733](https://github.com/sdispater/pendulum/pull/733) - Fixed DST handling in `start_of()/end_of()` methods. [#713](https://github.com/sdispater/pendulum/pull/713) ## [3.0.0a1] - 2022-11-23 ### Added - Added new testing helpers to time travel. [#626](https://github.com/sdispater/pendulum/pull/626) ### Changed - Dropped support for Python 2.7, 3.5 and 3.6. [#569](https://github.com/sdispater/pendulum/pull/569) - The `Timezone` class now relies on the native `zoneinfo.ZoneInfo` class. [#569](https://github.com/sdispater/pendulum/pull/569) - Renamed the `Period` class to `Interval`. [#676](https://github.com/sdispater/pendulum/pull/676) - Renamed the `period` helper to `interval`. [#676](https://github.com/sdispater/pendulum/pull/676) - Removed existing testing helpers: `test()` and `set_test_now()`. [#626](https://github.com/sdispater/pendulum/pull/626) ### Locales - Added the `sk` locale. [#575](https://github.com/sdispater/pendulum/pull/575) - Added the `ja` locale. [#610](https://github.com/sdispater/pendulum/pull/610) - Added the `he` locale. [#585](https://github.com/sdispater/pendulum/pull/585) - Added the `sv` locale. [#562](https://github.com/sdispater/pendulum/pull/562) ## [2.1.1] - 2020-07-13 ### Fixed - Fixed errors where invalid timezones were matched in `from_format()` ([#374](https://github.com/sdispater/pendulum/pull/374)). - Fixed errors when subtracting negative timedeltas ([#419](https://github.com/sdispater/pendulum/pull/419)). - Fixed errors in total units computation for durations with years and months ([#482](https://github.com/sdispater/pendulum/pull/482)). - Fixed an error where the `fold` attribute was overridden when using `replace()` ([#414](https://github.com/sdispater/pendulum/pull/414)). - Fixed an error where `now()` was not returning the correct result on DST transitions ([#483](https://github.com/sdispater/pendulum/pull/483)). - Fixed inconsistent typing annotation for the `parse()` function ([#452](https://github.com/sdispater/pendulum/pull/452)). ### Locales - Added the `pl` locale ([#459](https://github.com/sdispater/pendulum/pull/459)). ## [2.1.0] - 2020-03-07 ### Added - Added better typing and PEP-561 compliance ([#320](https://github.com/sdispater/pendulum/pull/320)). - Added the `is_anniversary()` method as an alias of `is_birthday()` ([#298](https://github.com/sdispater/pendulum/pull/298)). ### Changed - Dropped support for Python 3.4. - `is_utc()` will now return `True` for any datetime with an offset of 0, similar to the behavior in the `1.*` versions ([#295](https://github.com/sdispater/pendulum/pull/295)) - `Duration.in_words()` will now return `0 milliseconds` for empty durations. ### Fixed - Fixed various issues with timezone transitions for some edge cases ([#321](https://github.com/sdispater/pendulum/pull/321), ([#350](https://github.com/sdispater/pendulum/pull/350))). - Fixed out of bound detection for `nth_of("month")` ([#357](https://github.com/sdispater/pendulum/pull/357)). - Fixed an error where extra text was accepted in `from_format()` ([#372](https://github.com/sdispater/pendulum/pull/372)). - Fixed a recursion error when adding time to a `DateTime` with a fixed timezone ([#431](https://github.com/sdispater/pendulum/pull/431)). - Fixed errors where `Period` instances were not properly compared to other classes, especially `timedelta` instances ([#427](https://github.com/sdispater/pendulum/pull/427)). - Fixed deprecation warnings due to internal regexps ([#427](https://github.com/sdispater/pendulum/pull/427)). - Fixed an error where the `test()` helper would not unset the test instance when an exception was raised ([#445](https://github.com/sdispater/pendulum/pull/445)). - Fixed an error where the `week_of_month` attribute was not returning the correct value ([#446](https://github.com/sdispater/pendulum/pull/446)). - Fixed an error in the way the `Z` ISO-8601 UTC designator was not parsed as UTC ([#448](https://github.com/sdispater/pendulum/pull/448)). ### Locales - Added the `nl` locale. - Added the `it` locale. - Added the `id` locale. - Added the `nb` locale. - Added the `nn` locale. ## [2.0.5] - 2019-07-03 ### Fixed - Fixed ISO week dates not being parsed properly in `from_format()`. - Fixed loading of some timezones with empty posix spec. - Fixed deprecation warnings. ### Locales - Added RU locale. ## [2.0.4] - 2018-10-30 ### Fixed - Fixed `from_format()` not recognizing input strings when the specified pattern had escaped elements. - Fixed missing `x` token for string formatting. - Fixed reading timezone files. - Added support for parsing padded 2-digit days of the month with `from_format()` - Fixed `from_format()` trying to parse escaped tokens. - Fixed the `z` token timezone parsing in `from_format()` to allow underscores. - Fixed C extensions build errors. - Fixed `age` calculation for future dates. ## [2.0.3] - 2018-07-30 ### Fixed - Fixed handling of `pytz` timezones. - Fixed some formatter's tokens handling. - Fixed errors on some systems when retrieving timezone from localtime files. - Fixed `diff` methods. - Fixed `closest()/farthest()` methods. ## [2.0.2] - 2018-05-29 ### Fixed - Fixed the `weeks` property for negative `Period` instances. - Fixed `start_of()` methods not setting microseconds to 0. - Fixed errors on some systems when retrieving timezone from clock files. - Fixed parsing of partial time. - Fixed parsing not raising an error for week 53 for ordinary years. - Fixed string formatting not supporting `strftime` format. ## [2.0.1] - 2018-05-10 ### Fixed - Fixed behavior of the `YY` token in `from_format()`. - Fixed errors on some systems when retrieving timezone from clock files. ## [2.0.0] - 2018-05-08 ### Added - Added years and months support to durations. - Added the `test_local_timezone()` and `set_local_timezone()` helpers to ease testing. - Added support of ISO 8601 duration parsing. - Added support of ISO 8601 interval parsing. - Added a `local()` helper. - Added a `naive()` helper and a `naive()` method. - Added support for POSIX specification to extend timezones DST transitions. ### Changed - `Pendulum` class has been renamed to `DateTime`. - `Interval` class has been renamed to `Duration`. - Changed and improved the timezone system. - Removed the `create()` helper. - Removed the `utcnow()` helper. - `strict` keyword argument for `parse` has been renamed to `exact`. - `at()` now supports setting partial time. - `local`, `utc` and `is_dst` are now methods rather than properties (`is_local()`, `is_utc()`, `is_dst()`). - Changed the `repr` of most common objects. - Made the `strict` keyword argument for `parse` false by default, which means it will not fallback on the `dateutil` parser. - Improved performances of the `precise_diff()` helper. - The `alternative` formatter is now the default one. - `set_to_string_format()/reset_to_string_format()` methods have been removed. - `from_format()` now uses the alternative formatter tokens. - Removed `xrange()` method of the `Period` class and made `range()` a generator. - New locale system which uses CLDR data for most of the translations. - `diff_for_humans()` now returns `a few seconds` where appropriate. - Removed `Period.intersect()`. [Unreleased]: https://github.com/sdispater/pendulum/compare/3.0.0...master [3.0.0]: https://github.com/sdispater/pendulum/releases/tag/3.0.0 [3.0.0b1]: https://github.com/sdispater/pendulum/releases/tag/3.0.0b1 [3.0.0a1]: https://github.com/sdispater/pendulum/releases/tag/3.0.0a1 [2.1.1]: https://github.com/sdispater/pendulum/releases/tag/2.1.1 [2.1.0]: https://github.com/sdispater/pendulum/releases/tag/2.1.0 [2.0.5]: https://github.com/sdispater/pendulum/releases/tag/2.0.5 [2.0.4]: https://github.com/sdispater/pendulum/releases/tag/2.0.4 [2.0.3]: https://github.com/sdispater/pendulum/releases/tag/2.0.3 [2.0.2]: https://github.com/sdispater/pendulum/releases/tag/2.0.2 [2.0.1]: https://github.com/sdispater/pendulum/releases/tag/2.0.1 [2.0.0]: https://github.com/sdispater/pendulum/releases/tag/2.0.0 pendulum-3.0.0/LICENSE000066400000000000000000000020461453741033500143570ustar00rootroot00000000000000Copyright (c) 2015 Sébastien Eustace Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. pendulum-3.0.0/Makefile000066400000000000000000000007531453741033500150150ustar00rootroot00000000000000 # lists all available targets list: @sh -c "$(MAKE) -p no_targets__ | \ awk -F':' '/^[a-zA-Z0-9][^\$$#\/\\t=]*:([^=]|$$)/ {\ split(\$$1,A,/ /);for(i in A)print A[i]\ }' | grep -v '__\$$' | grep -v 'make\[1\]' | grep -v 'Makefile' | sort" # required for list no_targets__: lint-rust: cd rust && cargo fmt --all -- --check cd rust && cargo clippy --tests -- -D warnings format-rust: cd rust && cargo fmt --all cd rust && cargo clippy --tests --fix --allow-dirty -- -D warnings pendulum-3.0.0/README.rst000066400000000000000000000132221453741033500150370ustar00rootroot00000000000000Pendulum ######## .. image:: https://img.shields.io/pypi/v/pendulum.svg :target: https://pypi.python.org/pypi/pendulum .. image:: https://img.shields.io/pypi/l/pendulum.svg :target: https://pypi.python.org/pypi/pendulum .. image:: https://github.com/sdispater/pendulum/actions/workflows/tests.yml/badge.svg :alt: Pendulum Build status :target: https://github.com/sdispater/pendulum/actions Python datetimes made easy. Supports Python **3.8 and newer**. .. code-block:: python >>> import pendulum >>> now_in_paris = pendulum.now('Europe/Paris') >>> now_in_paris '2016-07-04T00:49:58.502116+02:00' # Seamless timezone switching >>> now_in_paris.in_timezone('UTC') '2016-07-03T22:49:58.502116+00:00' >>> tomorrow = pendulum.now().add(days=1) >>> last_week = pendulum.now().subtract(weeks=1) >>> past = pendulum.now().subtract(minutes=2) >>> past.diff_for_humans() '2 minutes ago' >>> delta = past - last_week >>> delta.hours 23 >>> delta.in_words(locale='en') '6 days 23 hours 58 minutes' # Proper handling of datetime normalization >>> pendulum.datetime(2013, 3, 31, 2, 30, tz='Europe/Paris') '2013-03-31T03:30:00+02:00' # 2:30 does not exist (Skipped time) # Proper handling of dst transitions >>> just_before = pendulum.datetime(2013, 3, 31, 1, 59, 59, 999999, tz='Europe/Paris') '2013-03-31T01:59:59.999999+01:00' >>> just_before.add(microseconds=1) '2013-03-31T03:00:00+02:00' Resources ========= * `Official Website `_ * `Documentation `_ * `Issue Tracker `_ Why Pendulum? ============= Native ``datetime`` instances are enough for basic cases but when you face more complex use-cases they often show limitations and are not so intuitive to work with. ``Pendulum`` provides a cleaner and more easy to use API while still relying on the standard library. So it's still ``datetime`` but better. Unlike other datetime libraries for Python, Pendulum is a drop-in replacement for the standard ``datetime`` class (it inherits from it), so, basically, you can replace all your ``datetime`` instances by ``DateTime`` instances in your code (exceptions exist for libraries that check the type of the objects by using the ``type`` function like ``sqlite3`` or ``PyMySQL`` for instance). It also removes the notion of naive datetimes: each ``Pendulum`` instance is timezone-aware and by default in ``UTC`` for ease of use. Pendulum also improves the standard ``timedelta`` class by providing more intuitive methods and properties. Limitations =========== Even though the ``DateTime`` class is a subclass of ``datetime`` there are some rare cases where it can't replace the native class directly. Here is a list (non-exhaustive) of the reported cases with a possible solution, if any: * ``sqlite3`` will use the ``type()`` function to determine the type of the object by default. To work around it you can register a new adapter: .. code-block:: python from pendulum import DateTime from sqlite3 import register_adapter register_adapter(DateTime, lambda val: val.isoformat(' ')) * ``mysqlclient`` (former ``MySQLdb``) and ``PyMySQL`` will use the ``type()`` function to determine the type of the object by default. To work around it you can register a new adapter: .. code-block:: python import MySQLdb.converters import pymysql.converters from pendulum import DateTime MySQLdb.converters.conversions[DateTime] = MySQLdb.converters.DateTime2literal pymysql.converters.conversions[DateTime] = pymysql.converters.escape_datetime * ``django`` will use the ``isoformat()`` method to store datetimes in the database. However since ``pendulum`` is always timezone aware the offset information will always be returned by ``isoformat()`` raising an error, at least for MySQL databases. To work around it you can either create your own ``DateTimeField`` or use the previous workaround for ``MySQLdb``: .. code-block:: python from django.db.models import DateTimeField as BaseDateTimeField from pendulum import DateTime class DateTimeField(BaseDateTimeField): def value_to_string(self, obj): val = self.value_from_object(obj) if isinstance(value, DateTime): return value.to_datetime_string() return '' if val is None else val.isoformat() Contributing ============ Contributions are welcome, especially with localization. Getting started --------------- To work on the Pendulum codebase, you'll want to clone the project locally and install the required dependencies via `poetry `_. .. code-block:: bash $ git clone git@github.com:sdispater/pendulum.git $ poetry install Localization ------------ If you want to help with localization, there are two different cases: the locale already exists or not. If the locale does not exist you will need to create it by using the ``clock`` utility: .. code-block:: bash ./clock locale create It will generate a directory in ``pendulum/locales`` named after your locale, with the following structure: .. code-block:: text / - custom.py - locale.py The ``locale.py`` file must not be modified. It contains the translations provided by the CLDR database. The ``custom.py`` file is the one you want to modify. It contains the data needed by Pendulum that are not provided by the CLDR database. You can take the `en `_ data as a reference to see which data is needed. You should also add tests for the created or modified locale. pendulum-3.0.0/clock000077500000000000000000000200031453741033500143640ustar00rootroot00000000000000#!/usr/bin/env python from __future__ import annotations import glob import json import os from babel.core import get_global from babel.dates import PATTERN_CHARS from babel.dates import tokenize_pattern from babel.localedata import LocaleDataDict from babel.localedata import load from babel.localedata import normalize_locale from babel.plural import PluralRule from babel.plural import _binary_compiler from babel.plural import _GettextCompiler from babel.plural import _unary_compiler from babel.plural import compile_zero from cleo.application import Application from cleo.commands.command import Command from cleo.helpers import argument from pendulum import __version__ class _LambdaCompiler(_GettextCompiler): """Compiles the expression to lambda function.""" compile_v = compile_zero compile_w = compile_zero compile_f = compile_zero compile_t = compile_zero compile_and = _binary_compiler("(%s and %s)") compile_or = _binary_compiler("(%s or %s)") compile_not = _unary_compiler("(not %s)") compile_mod = _binary_compiler("(%s %% %s)") def compile_relation(self, method, expr, range_list): code = _GettextCompiler.compile_relation(self, method, expr, range_list) code = code.replace("&&", "and") code = code.replace("||", "or") if method == "in": expr = self.compile(expr) code = f"({expr} == {expr} and {code})" return code class LocaleCreate(Command): name = "locale create" description = "Creates locale translations." arguments = [argument("locales", "Locales to dump.", optional=False, multiple=True)] TEMPLATE = """from .custom import translations as custom_translations \"\"\" {locale} locale file. It has been generated automatically and must not be modified directly. \"\"\" locale = {{ 'plural': {plural}, 'ordinal': {ordinal}, 'translations': {translations}, 'custom': custom_translations }} """ CUSTOM_TEMPLATE = """\"\"\" {locale} custom locale file. \"\"\" translations = {{}} """ LOCALE_DIR = os.path.join("pendulum", "locales") def handle(self): locales = self.argument("locales") if not locales: return for locale in locales: data = {} parts = locale.split("-") if len(parts) > 1: parts[1] = parts[1].upper() normalized = normalize_locale(locale.replace("-", "_")) if not normalized: self.line(f"Locale [{locale}] does not exist.") continue self.line(f"Generating {locale} locale.") content = LocaleDataDict(load(normalized)) # Pluralization rule rule = content["plural_form"] plural = self.plural_rule_to_lambda(rule) # Ordinal rule rule = content["ordinal_form"] ordinal = self.plural_rule_to_lambda(rule) # Getting days names days = content["days"]["format"] data["days"] = {} for fmt, names in days.items(): data["days"][fmt] = {} for value, name in sorted(names.items()): data["days"][fmt][value] = name # Getting months names months = content["months"]["format"] data["months"] = months # Units patterns = content["unit_patterns"] units = [ "year", "month", "week", "day", "hour", "minute", "second", "microsecond", ] data["units"] = {} for unit in units: pattern = patterns[f"duration-{unit}"]["long"] if "per" in pattern: del pattern["per"] data["units"][unit] = pattern # Relative data["relative"] = {} for key in content["date_fields"]: if key not in [ "year", "month", "week", "day", "hour", "minute", "second", ]: continue data["relative"][key] = content["date_fields"][key] # Day periods data["day_periods"] = content["day_periods"]["format"]["wide"] # Week data data["week_data"] = content["week_data"] result = self.TEMPLATE.format( locale=locale, plural=plural, ordinal=ordinal, translations=self.format_dict(data, tab=2), ) dest_dir = os.path.join(self.LOCALE_DIR, locale.replace("-", "_")) if not os.path.exists(dest_dir): os.mkdir(dest_dir) init = os.path.join(dest_dir, "__init__.py") main = os.path.join(dest_dir, "locale.py") custom = os.path.join(dest_dir, "custom.py") if not os.path.exists(init): with open(init, "w"): os.utime(init) with open(main, "w") as fw: fw.write(result) if not os.path.exists(custom): with open(custom, "w") as fw: fw.write(self.CUSTOM_TEMPLATE.format(locale=locale)) def format_dict(self, d, tab=1): s = ["{\n"] for k, v in d.items(): if isinstance(v, (dict, LocaleDataDict)): v = self.format_dict(v, tab + 1) else: v = repr(v) s.append(f"{' ' * tab}{k!r}: {v},\n") s.append(f'{" " * (tab - 1)}}}') return "".join(s) def plural_rule_to_lambda(self, rule): to_py = _LambdaCompiler().compile result = ["lambda n: "] for tag, ast in PluralRule.parse(rule).abstract: result.append(f"'{tag}' if {to_py(ast)} else ") result.append("'other'") return "".join(result) def convert_ldml_format(self, fmt): result = [] for tok_type, tok_value in tokenize_pattern(fmt): if tok_type == "chars": result.append(tok_value.replace("%", "%%")) elif tok_type == "field": fieldchar, fieldnum = tok_value limit = PATTERN_CHARS[fieldchar] if limit and fieldnum not in limit: raise ValueError( f"Invalid length for field: {(fieldchar * fieldnum)!r}" ) result.append( self.TOKENS_MAP.get(fieldchar * fieldnum, fieldchar * fieldnum) ) else: raise NotImplementedError(f"Unknown token type: {tok_type}") return "".join(result) class LocaleRecreate(Command): name = "locale recreate" description = "Recreate existing locales." def handle(self): # Listing locales locales_dir = os.path.join("pendulum", "locales") locales = glob.glob(os.path.join(locales_dir, "*", "locale.py")) locales = [os.path.basename(os.path.dirname(locale)) for locale in locales] self.call("locale create", "locales " + " ".join(locales)) class WindowsTzDump(Command): name = "windows dump-timezones" description = "Dumps the mapping of Windows timezones to IANA timezones." MAPPING_DIR = os.path.join("pendulum", "tz", "data") def handle(self): raw_tznames = get_global("windows_zone_mapping") sorted_names = sorted(raw_tznames.keys()) tznames = {} for name in sorted_names: tznames[name] = raw_tznames[name] mapping = json.dumps(tznames, indent=4).replace('"', "'") with open(os.path.join(self.MAPPING_DIR, "windows.py"), "w") as f: f.write(f"windows_timezones = {mapping}\n") app = Application("clock", __version__) app.add(LocaleCreate()) app.add(LocaleRecreate()) app.add(WindowsTzDump()) if __name__ == "__main__": app.run() pendulum-3.0.0/docs/000077500000000000000000000000001453741033500143005ustar00rootroot00000000000000pendulum-3.0.0/docs/Makefile000066400000000000000000000000241453741033500157340ustar00rootroot00000000000000html: mkdocs build pendulum-3.0.0/docs/docs/000077500000000000000000000000001453741033500152305ustar00rootroot00000000000000pendulum-3.0.0/docs/docs/addition_subtraction.md000066400000000000000000000037411453741033500217670ustar00rootroot00000000000000# Addition and Subtraction To easily add and subtract time, you can use the `add()` and `subtract()` methods. Each method returns a new `DateTime` instance. ```python >>> import pendulum >>> dt = pendulum.datetime(2012, 1, 31) >>> dt.to_datetime_string() '2012-01-31 00:00:00' >>> dt = dt.add(years=5) '2017-01-31 00:00:00' >>> dt = dt.add(years=1) '2018-01-31 00:00:00' >>> dt = dt.subtract(years=1) '2017-01-31 00:00:00' >>> dt = dt.subtract(years=5) '2012-01-31 00:00:00' >>> dt = dt.add(months=60) '2017-01-31 00:00:00' >>> dt = dt.add(months=1) '2017-02-28 00:00:00' >>> dt = dt.subtract(months=1) '2017-01-28 00:00:00' >>> dt = dt.subtract(months=60) '2012-01-28 00:00:00' >>> dt = dt.add(days=29) '2012-02-26 00:00:00' >>> dt = dt.add(days=1) '2012-02-27 00:00:00' >>> dt = dt.subtract(days=1) '2012-02-26 00:00:00' >>> dt = dt.subtract(days=29) '2012-01-28 00:00:00' >>> dt = dt.add(weeks=3) '2012-02-18 00:00:00' >>> dt = dt.add(weeks=1) '2012-02-25 00:00:00' >>> dt = dt.subtract(weeks=1) '2012-02-18 00:00:00' >>> dt = dt.subtract(weeks=3) '2012-01-28 00:00:00' >>> dt = dt.add(hours=24) '2012-01-29 00:00:00' >>> dt = dt.add(hours=1) '2012-02-25 01:00:00' >>> dt = dt.subtract(hours=1) '2012-02-29 00:00:00' >>> dt = dt.subtract(hours=24) '2012-01-28 00:00:00' >>> dt = dt.add(minutes=61) '2012-01-28 01:01:00' >>> dt = dt.add(minutes=1) '2012-01-28 01:02:00' >>> dt = dt.subtract(minutes=1) '2012-01-28 01:01:00' >>> dt = dt.subtract(minutes=24) '2012-01-28 00:00:00' >>> dt = dt.add(seconds=61) '2012-01-28 00:01:01' >>> dt = dt.add(seconds=1) '2012-01-28 00:01:02' >>> dt = dt.subtract(seconds=1) '2012-01-28 00:01:01' >>> dt = dt.subtract(seconds=61) '2012-01-28 00:00:00' >>> dt = dt.add(years=3, months=2, days=6, hours=12, minutes=31, seconds=43) '2015-04-03 12:31:43' >>> dt = dt.subtract(years=3, months=2, days=6, hours=12, minutes=31, seconds=43) '2012-01-28 00:00:00' ``` !!!note Passing negative values to `add()` is also possible and will act exactly like `subtract()` pendulum-3.0.0/docs/docs/attributes_properties.md000066400000000000000000000033451453741033500222210ustar00rootroot00000000000000# Attributes and Properties Pendulum gives access to more attributes and properties than the default ``datetime`` class. ```python >>> import pendulum >>> dt = pendulum.parse('2012-09-05T23:26:11.123789') # These properties specifically return integers >>> dt.year 2012 >>> dt.month 9 >>> dt.day 5 >>> dt.hour 23 >>> dt.minute 26 >>> dt.second 11 >>> dt.microsecond 123789 >>> dt.day_of_week 3 >>> dt.day_of_year 248 >>> dt.week_of_month 1 >>> dt.week_of_year 36 >>> dt.days_in_month 30 >>> dt.timestamp() 1346887571.123789 >>> dt.float_timestamp 1346887571.123789 >>> dt.int_timestamp 1346887571 >>> pendulum.datetime(1975, 5, 21).age 41 # calculated vs now in the same tz >>> dt.quarter 3 # Returns an int of seconds difference from UTC (+/- sign included) >>> pendulum.from_timestamp(0).offset 0 >>> pendulum.from_timestamp(0, 'America/Toronto').offset -18000 # Returns a float of hours difference from UTC (+/- sign included) >>> pendulum.from_timestamp(0, 'America/Toronto').offset_hours -5.0 >>> pendulum.from_timestamp(0, 'Australia/Adelaide').offset_hours 9.5 # Gets the timezone instance >>> pendulum.now().timezone >>> pendulum.now().tz # Gets the timezone name >>> pendulum.now().timezone_name # Indicates if daylight savings time is on >>> dt = pendulum.datetime(2012, 1, 1, tz='America/Toronto') >>> dt.is_dst() False >>> dt = pendulum.datetime(2012, 9, 1, tz='America/Toronto') >>> dt.is_dst() True # Indicates if the instance is in the same timezone as the local timezone >>> pendulum.now().is_local() True >>> pendulum.now('Europe/London').is_local() False # Indicates if the instance is in the UTC timezone >>> pendulum.now().is_utc() False >>> pendulum.now('Europe/London').is_local() False >>> pendulum.now('UTC').is_utc() True ``` pendulum-3.0.0/docs/docs/comparison.md000066400000000000000000000031511453741033500177240ustar00rootroot00000000000000# Comparison Simple comparison is offered up via the basic operators. Remember that the comparison is done in the UTC timezone so things aren't always as they seem. ```python >>> import pendulum >>> first = pendulum.datetime(2012, 9, 5, 23, 26, 11, 0, tz='America/Toronto') >>> second = pendulum.datetime(2012, 9, 5, 20, 26, 11, 0, tz='America/Vancouver') >>> first.to_datetime_string() '2012-09-05 23:26:11' >>> first.timezone_name 'America/Toronto' >>> second.to_datetime_string() '2012-09-05 20:26:11' >>> second.timezone_name 'America/Vancouver' >>> first == second True >>> first != second False >>> first > second False >>> first >= second True >>> first < second False >>> first <= second True >>> first = first.on(2012, 1, 1).at(0, 0, 0) >>> second = second.on(2012, 1, 1).at(0, 0, 0) # tz is still America/Vancouver for second >>> first == second False >>> first != second True >>> first > second False >>> first >= second False >>> first < second True >>> first <= second True ``` To handle the most used cases there are some simple helper functions. For the methods that compare to `now()` (ex. `is_today()`) in some manner the `now()` is created in the same timezone as the instance. ```python >>> import pendulum >>> dt = pendulum.now() >>> dt.is_past() >>> dt.is_leap_year() >>> born = pendulum.datetime(1987, 4, 23) >>> not_birthday = pendulum.datetime(2014, 9, 26) >>> birthday = pendulum.datetime(2014, 4, 23) >>> past_birthday = pendulum.now().subtract(years=50) >>> born.is_birthday(not_birthday) False >>> born.is_birthday(birthday) True >>> past_birthday.is_birthday() # Compares to now by default True ``` pendulum-3.0.0/docs/docs/difference.md000066400000000000000000000065621453741033500176550ustar00rootroot00000000000000# Difference The `diff()` method returns an [Interval](#interval) instance that represents the total duration between two `DateTime` instances. This interval can be then expressed in various units. These interval methods always return *the total difference expressed* in the specified time requested. All values are truncated and not rounded. The `diff()` method has a default first parameter which is the `DateTime` instance to compare to, or `None` if you want to use `now()`. The 2nd parameter is optional and indicates if you want the return value to be the absolute value or a relative value that might have a `-` (negative) sign if the passed in date is less than the current instance. This will default to `True`, return the absolute value. ```python >>> import pendulum >>> dt_ottawa = pendulum.datetime(2000, 1, 1, tz='America/Toronto') >>> dt_vancouver = pendulum.datetime(2000, 1, 1, tz='America/Vancouver') >>> dt_ottawa.diff(dt_vancouver).in_hours() 3 >>> dt_ottawa.diff(dt_vancouver, False).in_hours() 3 >>> dt_vancouver.diff(dt_ottawa, False).in_hours() -3 >>> dt = pendulum.datetime(2012, 1, 31, 0) >>> dt.diff(dt.add(months=1)).in_days() 29 >>> dt.diff(dt.subtract(months=1), False).in_days() -31 >>> dt = pendulum.datetime(2012, 4, 30, 0) >>> dt.diff(dt.add(months=1)).in_days() 30 >>> dt.diff(dt.add(weeks=1)).in_days() 7 >>> dt = pendulum.datetime(2012, 1, 1, 0) >>> dt.diff(dt.add(seconds=59)).in_minutes() 0 >>> dt.diff(dt.add(seconds=60)).in_minutes() 1 >>> dt.diff(dt.add(seconds=119)).in_minutes() 1 >>> dt.diff(dt.add(seconds=120)).in_minutes() 2 ``` Difference for Humans --------------------- The `diff_for_humans()` method will add a phrase after the difference value relative to the instance and the passed in instance. There are 4 possibilities: * When comparing a value in the past to default now: * 1 hour ago * 5 months ago * When comparing a value in the future to default now: * 1 hour from now * 5 months from now * When comparing a value in the past to another value: * 1 hour before * 5 months before * When comparing a value in the future to another value: * 1 hour after * 5 months after You may also pass `True` as a 2nd parameter to remove the modifiers `ago`, `from now`, etc. ```python >>> import pendulum # The most typical usage is for comments # The instance is the date the comment was created # and its being compared to default now() >>> pendulum.now().subtract(days=1).diff_for_humans() '1 day ago' >>> pendulum.now().diff_for_humans(pendulum.now().subtract(years=1)) '1 year after' >>> dt = pendulum.datetime(2011, 8, 1) >>> dt.diff_for_humans(dt.add(months=1)) '1 month before' >>> dt.diff_for_humans(dt.subtract(months=1)) '1 month after' >>> pendulum.now().add(seconds=5).diff_for_humans() '5 seconds from now' >>> pendulum.now().subtract(days=24).diff_for_humans() '3 weeks ago' >>> pendulum.now().subtract(days=24).diff_for_humans(absolute=True) '3 weeks' ``` You can also change the locale of the string either globally by using `pendulum.set_locale('fr')` before the `diff_for_humans()` call or specifically for the call by passing the `locale` keyword argument. See the [Localization](#localization) section for more detail. ```python >>> import pendulum >>> pendulum.set_locale('de') >>> pendulum.now().add(years=1).diff_for_humans() 'in 1 Jahr' >>> pendulum.now().add(years=1).diff_for_humans(locale='fr') 'dans 1 an' ``` pendulum-3.0.0/docs/docs/duration.md000066400000000000000000000067651453741033500174150ustar00rootroot00000000000000# Duration The `Duration` class is inherited from the native `timedelta` class. It has many improvements over the base class. !!!note Even though, it inherits from the `timedelta` class, its behavior is slightly different. The more important to notice is that the native normalization does not happen, this is so that it feels more intuitive. ```python >>> import pendulum >>> from datetime import datetime >>> d1 = datetime(2012, 1, 1, 1, 2, 3, tzinfo=pytz.UTC) >>> d2 = datetime(2011, 12, 31, 22, 2, 3, tzinfo=pytz.UTC) >>> delta = d2 - d1 >>> delta.days -1 >>> delta.seconds 75600 >>> d1 = pendulum.datetime(2012, 1, 1, 1, 2, 3) >>> d2 = pendulum.datetime(2011, 12, 31, 22, 2, 3) >>> delta = d2 - d1 >>> delta.days 0 >>> delta.hours -3 ``` ## Instantiation To create a `Duration` instance, you can use the `duration()` helper: ```python >>> import pendulum >>> it = pendulum.duration(days=1177, seconds=7284, microseconds=1234) ``` !!!note Unlike the native `timedelta` class, durations support specifying years and months. ```python >>> import pendulum >>> it = pendulum.duration(years=2, months=3) ``` However, to maintain compatibility, native methods and properties will make approximations: ```python >>> it.days 820 >>> it.total_seconds() 70848000.0 ``` ## Properties and Duration Methods The `Duration` class brings more properties than the default `days`, `seconds` and `microseconds`. ```python >>> import pendulum >>> it = pendulum.duration( ... years=2, months=3, ... days=1177, seconds=7284, microseconds=1234 ... ) >>> it.years 2 >>> it.months 3 # Weeks are based on the total of days # It does not take into account years and months >>> it.weeks 168 # Days, just like in timedelta, represents the total of days # in the duration. If years and/or months are specified # it will use an approximation >>> it.days 1997 # If you want the remaining days not included in full weeks >>> it.remaining_days 1 >>> # The remaining number in each unit >>> it.hours 2 >>> it.minutes 1 # Seconds are, like days, a special case and the default # property will return the whole value of remaining # seconds just like the timedelta class for compatibility >>> it.seconds 7284 # If you want the number of seconds not included # in hours and minutes >>> it.remaining_seconds 24 >>> it.microseconds 1234 ``` If you want to get the duration in each supported unit you can use the appropriate methods. ```python # Each method returns a float like the native # total_seconds() method >>> it.total_weeks() 168.15490079569113 >>> it.total_days() 1177.0843055698379 >>> it.total_hours() 28250.02333367611 >>> it.total_minutes() 1695001.4000205665 >>> it.total_seconds() 101700084.001234 ``` Similarly, the `in_xxx()` methods return the total duration in each supported unit as a truncated integer. ```python >>> it.in_weeks() 168 >>> it.in_days() 1997 >>> it.in_hours() 28250 >>> it.in_minutes() 1695001 >>> it.in_seconds() 101700084 ``` It also has a handy `in_words()` method, which determines the duration representation when printed. ```python >>> import pendulum >>> pendulum.set_locale('fr') >>> it = pendulum.duration(days=1177, seconds=7284, microseconds=1234) >>> it.in_words() '168 semaines 1 jour 2 heures 1 minute 24 secondes' >>> print(it) '168 semaines 1 jour 2 heures 1 minute 24 secondes' >>> it.in_words(locale='de') '168 Wochen 1 Tag 2 Stunden 1 Minute 24 Sekunden' ``` pendulum-3.0.0/docs/docs/fluent_helpers.md000066400000000000000000000031041453741033500205670ustar00rootroot00000000000000# Fluent helpers Pendulum provides helpers that return a new instance with some attributes modified compared to the original instance. However, none of these helpers, with the exception of explicitly setting the timezone, will change the timezone of the instance. Specifically, setting the timestamp will not set the corresponding timezone to UTC. ```python >>> import pendulum >>> dt = pendulum.now() >>> dt.set(year=1975, month=5, day=21).to_datetime_string() '1975-05-21 13:45:18' >>> dt.set(hour=22, minute=32, second=5).to_datetime_string() '2016-11-16 22:32:05' ``` You can also use the `on()` and `at()` methods to change the date and the time respectively ```python >>> dt.on(1975, 5, 21).at(22, 32, 5).to_datetime_string() '1975-05-21 22:32:05' >>> dt.at(10).to_datetime_string() '2016-11-16 10:00:00' >>> dt.at(10, 30).to_datetime_string() '2016-11-16 10:30:00' ``` You can also modify the timezone. ```python >>> dt.set(tz='Europe/London') ``` Setting the timezone just modifies the timezone information without making any conversion, while `in_timezone()` (or `in_tz()`) converts the time in the appropriate timezone. ```python >>> import pendulum >>> dt = pendulum.datetime(2013, 3, 31, 2, 30) >>> print(dt) '2013-03-31T02:30:00+00:00' >>> dt = dt.set(tz='Europe/Paris') >>> print(dt) '2013-03-31T03:30:00+02:00' >>> dt = dt.in_tz('Europe/Paris') >>> print(dt) '2013-03-31T04:30:00+02:00' >>> dt = dt.set(tz='Europe/Paris').set(tz='UTC') >>> print(dt) '2013-03-31T03:30:00+00:00' >>> dt = dt.in_tz('Europe/Paris').in_tz('UTC') >>> print(dt) '2013-03-31T02:30:00+00:00' ``` pendulum-3.0.0/docs/docs/index.md000066400000000000000000000006451453741033500166660ustar00rootroot00000000000000{!docs/installation.md!} {!docs/introduction.md!} {!docs/instantiation.md!} {!docs/parsing.md!} {!docs/localization.md!} {!docs/attributes_properties.md!} {!docs/fluent_helpers.md!} {!docs/string_formatting.md!} {!docs/comparison.md!} {!docs/addition_subtraction.md!} {!docs/difference.md!} {!docs/modifiers.md!} {!docs/timezones.md!} {!docs/duration.md!} {!docs/interval.md!} {!docs/testing.md!} {!docs/limitations.md!} pendulum-3.0.0/docs/docs/installation.md000066400000000000000000000012021453741033500202460ustar00rootroot00000000000000# Installation Installing `pendulum` is quite simple: ```bash $ pip install pendulum ``` or, if you are using [poetry](https://python-poetry.org): ```bash $ poetry add pendulum ``` ## Optional features Pendulum provides optional features that you must explicitly require in order to use them. These optional features are: - `test`: Provides a set of helpers to make testing easier by allowing you to control the flow of time. You can install them by specifying them when installing Pendulum ```bash $ pip install pendulum[test] ``` or, if you are using [poetry](https://python-poetry.org): ```bash $ poetry add pendulum[test] ``` pendulum-3.0.0/docs/docs/instantiation.md000066400000000000000000000070521453741033500204420ustar00rootroot00000000000000# Instantiation There are several different methods available to create a new `DateTime` instance. First there is the main `datetime()` helper. ```python >>> import pendulum >>> dt = pendulum.datetime(2015, 2, 5) >>> isinstance(dt, datetime) True >>> dt.timezone.name 'UTC' ``` `datetime()` sets the time to `00:00:00` if it's not specified, and the timezone (the `tz` keyword argument) to `UTC`. Otherwise it can be a `Timezone` instance or simply a string timezone value. ```python >>> import pendulum >>> pendulum.datetime(2015, 2, 5, tz='Europe/Paris') >>> tz = pendulum.timezone('Europe/Paris') >>> pendulum.datetime(2015, 2, 5, tz=tz) ``` !!!note Supported strings for timezones are the one provided by the [IANA time zone database](https://www.iana.org/time-zones). The special `local` string is also supported and will return your current timezone. !!!warning The `tz` argument is keyword-only, unlike in version `1.x` The `local()` helper is similar to `datetime()` but automatically sets the timezone to the local timezone. ```python >>> import pendulum >>> dt = pendulum.local(2015, 2, 5) >>> print(dt.timezone.name) 'America/Toronto' ``` !!!note `local()` is just an alias for `datetime(..., tz='local')`. There is also the `now()` method. ```python >>> import pendulum >>> now = pendulum.now() >>> now_in_london_tz = pendulum.now('Europe/London') >>> now_in_london_tz.timezone_name 'Europe/London' ``` To accompany `now()`, a few other static instantiation helpers exist to create known instances. The only thing to really notice here is that `today()`, `tomorrow()` and `yesterday()`, besides behaving as expected, all accept a timezone parameter and each has their time value set to `00:00:00`. ```python >>> now = pendulum.now() >>> print(now) '2016-06-28T16:51:45.978473-05:00' >>> today = pendulum.today() >>> print(today) '2016-06-28T00:00:00-05:00' >>> tomorrow = pendulum.tomorrow('Europe/London') >>> print(tomorrow) '2016-06-29T00:00:00+01:00' >>> yesterday = pendulum.yesterday() >>> print(yesterday) '2016-06-27T00:00:00-05:00' ``` Pendulum enforces timezone aware datetimes, and using them is the preferred and recommended way of using the library. However, if you really need a **naive** `DateTime` object, the `naive()` helper is there for you. ```python >>> import pendulum >>> naive = pendulum.naive(2015, 2, 5) >>> naive.timezone None ``` The next helper, `from_format()`, is similar to the native `datetime.strptime()` function but uses custom tokens to create a `DateTime` instance. ```python >>> dt = pendulum.from_format('1975-05-21 22', 'YYYY-MM-DD HH') >>> print(dt) '1975-05-21T22:00:00+00:00' ``` !!!note To see all the available tokens, you can check the [Formatter](#formatter) section. It also accepts a `tz` keyword argument to specify the timezone: ```python >>> dt = pendulum.from_format('1975-05-21 22', 'YYYY-MM-DD HH', tz='Europe/London') '1975-05-21T22:00:00+01:00' ``` The final helper is for working with unix timestamps. `from_timestamp()` will create a `DateTime` instance equal to the given timestamp and will set the timezone as well or default it to `UTC`. ```python >>> dt = pendulum.from_timestamp(-1) >>> print(dt) '1969-12-31T23:59:59+00:00' >>> dt = pendulum.from_timestamp(-1, tz='Europe/London') >>> print(dt) '1970-01-01T00:59:59+01:00' ``` Finally, if you find yourself inheriting a `datetime.datetime` instance, you can create a `DateTime` instance via the `instance()` function. ```python >>> dt = datetime(2008, 1, 1) >>> p = pendulum.instance(dt) >>> print(p) '2008-01-01T00:00:00+00:00' ``` pendulum-3.0.0/docs/docs/interval.md000066400000000000000000000073571453741033500174120ustar00rootroot00000000000000# Interval When you subtract a `DateTime` instance from another, or use the `diff()` method, it will return an `Interval` instance. It inherits from the [Duration](#duration) class with the added benefit that it is aware of the instances that generated it, so that it can give access to more methods and properties: ```python >>> import pendulum >>> start = pendulum.datetime(2000, 11, 20) >>> end = pendulum.datetime(2016, 11, 5) >>> interval = end - start >>> interval.years 15 >>> interval.months 11 >>> interval.in_years() 15 >>> interval.in_months() 191 # Note that the weeks property # will change compared to the Duration class >>> interval.weeks 2 # 832 for the duration # However the days property will still remain the same # to keep the compatibility with the timedelta class >>> interval.days 5829 ``` Be aware that an interval, just like an duration, is compatible with the `timedelta` class regarding its attributes. However, its custom attributes (like `remaining_days`) will be aware of any DST transitions that might have occurred and adjust accordingly. Let's take an example: ```python >>> import pendulum >>> start = pendulum.datetime(2017, 3, 7, tz='America/Toronto') >>> end = start.add(days=6) >>> interval = end - start # timedelta properties >>> interval.days 5 >>> interval.seconds 82800 # interval properties >>> interval.remaining_days 6 >>> interval.hours 0 >>> interval.remaining_seconds 0 ``` !!!warning Due to their nature (fixed duration between two datetimes), most arithmetic operations will return a `Duration` instead of an `Interval`. ```python >>> import pendulum >>> dt1 = pendulum.datetime(2016, 8, 7, 12, 34, 56) >>> dt2 = dt1.add(days=6, seconds=34) >>> interval = pendulum.interval(dt1, dt2) >>> interval * 2 Duration(weeks=1, days=5, minutes=1, seconds=8) ``` ## Instantiation You can create an instance by using the `interval()` helper: ```python >>> import pendulum >>> start = pendulum.datetime(2000, 1, 1) >>> end = pendulum.datetime(2000, 1, 31) >>> interval = pendulum.interval(start, end) ``` You can also make an inverted interval: ```python >>> interval = pendulum.interval(end, start) >>> interval.remaining_days -2 ``` If you have inverted dates but want to make sure that the interval is positive, you should set the `absolute` keyword argument to `True`: ```python >>> interval = pendulum.interval(end, start, absolute=True) >>> interval.remaining_days 2 ``` ## Range If you want to iterate over a interval, you can use the `range()` method: ```python >>> import pendulum >>> start = pendulum.datetime(2000, 1, 1) >>> end = pendulum.datetime(2000, 1, 10) >>> interval = pendulum.interval(start, end) >>> for dt in interval.range('days'): >>> print(dt) '2000-01-01T00:00:00+00:00' '2000-01-02T00:00:00+00:00' '2000-01-03T00:00:00+00:00' '2000-01-04T00:00:00+00:00' '2000-01-05T00:00:00+00:00' '2000-01-06T00:00:00+00:00' '2000-01-07T00:00:00+00:00' '2000-01-08T00:00:00+00:00' '2000-01-09T00:00:00+00:00' '2000-01-10T00:00:00+00:00' ``` !!!note Supported units for `range()` are: `years`, `months`, `weeks`, `days`, `hours`, `minutes`, `seconds` and `microseconds` You can pass an amount for the passed unit to control the length of the gap: ```python >>> for dt in interval.range('days', 2): >>> print(dt) '2000-01-01T00:00:00+00:00' '2000-01-03T00:00:00+00:00' '2000-01-05T00:00:00+00:00' '2000-01-07T00:00:00+00:00' '2000-01-09T00:00:00+00:00' ``` You can also directly iterate over the `Interval` instance, the unit will be `days` in this case: ```python >>> for dt in interval: >>> print(dt) ``` You can check if a `DateTime` instance is inside a interval using the `in` keyword: ```python >>> dt = pendulum.datetime(2000, 1, 4) >>> dt in interval True ``` pendulum-3.0.0/docs/docs/introduction.md000066400000000000000000000013111453741033500202670ustar00rootroot00000000000000# Introduction Pendulum is a Python package to ease datetimes manipulation. It provides classes that are drop-in replacements for the native ones (they inherit from them). Special care has been taken to ensure timezones are handled correctly, and are based on the underlying `tzinfo` implementation. For example, all comparisons are done in `UTC` or in the timezone of the datetime being used. ```python >>> import pendulum >>> dt_toronto = pendulum.datetime(2012, 1, 1, tz='America/Toronto') >>> dt_vancouver = pendulum.datetime(2012, 1, 1, tz='America/Vancouver') >>> print(dt_vancouver.diff(dt_toronto).in_hours()) 3 ``` The default timezone, except when using the `now()` method, will always be `UTC`. pendulum-3.0.0/docs/docs/limitations.md000066400000000000000000000033641453741033500201140ustar00rootroot00000000000000# Limitations Even though the `DateTime` class is a subclass of `datetime`, there are some rare cases where it can't replace the native class directly. Here is a list (non-exhaustive) of the reported cases with a possible solution, if any: * `sqlite3` will use the `type()` function to determine the type of the object by default. To work around it you can register a new adapter: ```python import pendulum from sqlite3 import register_adapter register_adapter(pendulum.DateTime, lambda val: val.isoformat(' ')) ``` * `mysqlclient` (former `MySQLdb`) and `PyMySQL` will use the `type()` function to determine the type of the object by default. To work around it you can register a new adapter: ```python import pendulum import MySQLdb.converters import pymysql.converters MySQLdb.converters.conversions[pendulum.DateTime] = MySQLdb.converters.DateTime2literal pymysql.converters.conversions[pendulum.DateTime] = pymysql.converters.escape_datetime ``` * `django` will use the `isoformat()` method to store datetimes in the database. However, since `pendulum` is always timezone aware, the offset information will always be returned by `isoformat()` raising an error, at least for MySQL databases. To work around it, you can either create your own `DateTimeField` or use the previous workaround for `MySQLdb`: ```python import pendulum from django.db.models import DateTimeField as BaseDateTimeField class DateTimeField(BaseDateTimeField): def value_to_string(self, obj): val = self.value_from_object(obj) if isinstance(value, pendulum.DateTime): return value.format('YYYY-MM-DD HH:mm:ss') return '' if val is None else val.isoformat() ``` pendulum-3.0.0/docs/docs/localization.md000066400000000000000000000015301453741033500202410ustar00rootroot00000000000000# Localization Localization occurs when using the `format()` method which accepts a `locale` keyword. ```python >>> import pendulum >>> dt = pendulum.datetime(1975, 5, 21) >>> dt.format('dddd DD MMMM YYYY', locale='de') 'Mittwoch 21 Mai 1975' >>> dt.format('dddd DD MMMM YYYY') 'Wednesday 21 May 1975' ``` `diff_for_humans()` is also localized, you can set the locale by using `pendulum.set_locale()`. ```python >>> import pendulum >>> pendulum.set_locale('de') >>> pendulum.now().add(years=1).diff_for_humans() 'in 1 Jahr' >>> pendulum.set_locale('en') ``` However, you might not want to set the locale globally. The `diff_for_humans()` method accepts a `locale` keyword argument to use a locale for a specific call. ```python >>> pendulum.set_locale('de') >>> dt = pendulum.now().add(years=1) >>> dt.diff_for_humans(locale='fr') 'dans 1 an' ``` pendulum-3.0.0/docs/docs/modifiers.md000066400000000000000000000037051453741033500175400ustar00rootroot00000000000000# Modifiers This group of methods performs helpful modifications to a copy of the current instance. You'll notice that the `start_of()`, `next()` and `previous()` methods set the time to `00:00:00` and the `end_of()` methods set the time to `23:59:59.999999`. The only one slightly different is the `average()` method. It returns the middle date between itself and the provided `DateTime` argument. ```python >>> import pendulum >>> dt = pendulum.datetime(2012, 1, 31, 12, 0, 0) >>> dt.start_of('day') '2012-01-31 00:00:00' >>> dt.end_of('day') '2012-01-31 23:59:59' >>> dt.start_of('month') '2012-01-01 00:00:00' >>> dt.end_of('month') '2012-01-31 23:59:59' >>> dt.start_of('year') '2012-01-01 00:00:00' >>> dt.end_of('year') '2012-12-31 23:59:59' >>> dt.start_of('decade') '2010-01-01 00:00:00' >>> dt.end_of('decade') '2019-12-31 23:59:59' >>> dt.start_of('century') '2000-01-01 00:00:00' >>> dt.end_of('century') '2099-12-31 23:59:59' >>> dt.start_of('week') '2012-01-30 00:00:00' >>> dt.day_of_week == pendulum.MONDAY True # ISO8601 week starts on Monday >>> dt.end_of('week') '2012-02-05 23:59:59' >>> dt.day_of_week == pendulum.SUNDAY True # ISO8601 week ends on SUNDAY >>> dt.next(pendulum.WEDNESDAY) '2012-02-01 00:00:00' >>> dt.day_of_week == pendulum.WEDNESDAY True >>> dt = pendulum.datetime(2012, 1, 1, 12, 0, 0) dt.next() '2012-01-08 00:00:00' >>> dt.next(keep_time=True) '2012-01-08T12:00:00+00:00' >>> dt = pendulum.datetime(2012, 1, 31, 12, 0, 0) >>> dt.previous(pendulum.WEDNESDAY) '2012-01-25 00:00:00' >>> dt.day_of_week == pendulum.WEDNESDAY True >>> dt = pendulum.datetime(2012, 1, 1, 12, 0, 0) >>> dt.previous() '2011-12-25 00:00:00' >>> dt.previous(keep_time=True) '2011-12-25 12:00:00' >>> start = pendulum.datetime(2014, 1, 1) >>> end = pendulum.datetime(2014, 1, 30) >>> start.average(end) '2014-01-15 12:00:00' # others that are defined that are similar # and tha accept month, quarter and year units # first_of(), last_of(), nth_of() ``` pendulum-3.0.0/docs/docs/parsing.md000066400000000000000000000106301453741033500172150ustar00rootroot00000000000000# Parsing The library natively supports the RFC 3339 format, most ISO 8601 formats and some other common formats. ```python >>> import pendulum >>> dt = pendulum.parse('1975-05-21T22:00:00') >>> print(dt) '1975-05-21T22:00:00+00:00 # You can pass a tz keyword to specify the timezone >>> dt = pendulum.parse('1975-05-21T22:00:00', tz='Europe/Paris') >>> print(dt) '1975-05-21T22:00:00+01:00' # Not ISO 8601 compliant but common >>> dt = pendulum.parse('1975-05-21 22:00:00') ``` If you pass a non-standard or more complicated string, it will raise an exception, so it is advised to use the `from_format()` helper instead. However, if you want the library to fall back on the [dateutil](https://dateutil.readthedocs.io) parser, you have to pass `strict=False`. ```python >>> import pendulum >>> dt = pendulum.parse('31-01-01') Traceback (most recent call last): ... ParserError: Unable to parse string [31-01-01] >>> dt = pendulum.parse('31-01-01', strict=False) >>> print(dt) '2031-01-01T00:00:00+00:00' ``` ## RFC 3339 | String | Output | | --------------------------------- | ------------------------------------------| | 1996-12-19T16:39:57-08:00 | 1996-12-19T16:39:57-08:00 | | 1990-12-31T23:59:59Z | 1990-12-31T23:59:59+00:00 | ## ISO 8601 ### Datetime | String | Output | | --------------------------------- | ----------------------------------------- | | 20161001T143028+0530 | 2016-10-01T14:30:28+05:30 | | 20161001T14 | 2016-10-01T14:00:00+00:00 | ### Date | String | Output | | --------------------------------- | ----------------------------------------- | | 2012 | 2012-01-01T00:00:00+00:00 | | 2012-05-03 | 2012-05-03T00:00:00+00:00 | | 20120503 | 2012-05-03T00:00:00+00:00 | | 2012-05 | 2012-05-01T00:00:00+00:00 | ### Ordinal day | String | Output | | ---------------------------------- | ----------------------------------------- | | 2012-007 | 2012-01-07T00:00:00+00:00 | | 2012007 | 2012-01-07T00:00:00+00:00 | ### Week number | String | Output | | --------------------------------- | ----------------------------------------- | | 2012-W05 | 2012-01-30T00:00:00+00:00 | | 2012W05 | 2012-01-30T00:00:00+00:00 | | 2012-W05-5 | 2012-02-03T00:00:00+00:00 | | 2012W055 | 2012-02-03T00:00:00+00:00 | ### Time When passing only time information the date will default to today. | String | Output | | --------------------------------- | ------------------------------------------ | | 00:00 | 2016-12-17T00:00:00+00:00 | | 12:04:23 | 2016-12-17T12:04:23+00:00 | | 120423 | 2016-12-17T12:04:23+00:00 | | 12:04:23.45 | 2016-12-17T12:04:23.450000+00:00 | ### Intervals | String | Output | | ----------------------------------------- | ------------------------------------------------------ | | 2007-03-01T13:00:00Z/2008-05-11T15:30:00Z | 2007-03-01T13:00:00+00:00 -> 2008-05-11T15:30:00+00:00 | | 2008-05-11T15:30:00Z/P1Y2M10DT2H30M | 2008-05-11T15:30:00+00:00 -> 2009-07-21T18:00:00+00:00 | | P1Y2M10DT2H30M/2008-05-11T15:30:00Z | 2007-03-01T13:00:00+00:00 -> 2008-05-11T15:30:00+00:00 | !!!note You can pass the ``exact`` keyword argument to ``parse()`` to get the exact type that the string represents: ```python >>> import pendulum >>> pendulum.parse('2012-05-03', exact=True) Date(2012, 05, 03) >>> pendulum.parse('12:04:23', exact=True) Time(12, 04, 23) ``` pendulum-3.0.0/docs/docs/string_formatting.md000066400000000000000000000166111453741033500213170ustar00rootroot00000000000000# String formatting The `__str__` magic method is defined to allow `DateTime` instances to be printed as a pretty date string when used in a string context. The default string representation is the same as the one returned by the `isoformat()` method. ```python >>> import pendulum >>> dt = pendulum.datetime(1975, 12, 25, 14, 15, 16) >>> print(dt) '1975-12-25T14:15:16+00:00' >>> dt.to_date_string() '1975-12-25' >>> dt.to_formatted_date_string() 'Dec 25, 1975' >>> dt.to_time_string() '14:15:16' >>> dt.to_datetime_string() '1975-12-25 14:15:16' >>> dt.to_day_datetime_string() 'Thu, Dec 25, 1975 2:15 PM' # You can also use the format() method >>> dt.format('dddd Do [of] MMMM YYYY HH:mm:ss A') 'Thursday 25th of December 1975 02:15:16 PM' # Of course, the strftime method is still available >>> dt.strftime('%A %-d%t of %B %Y %I:%M:%S %p') 'Thursday 25th of December 1975 02:15:16 PM' ``` !!!note For localization support see the [Localization](#localization) section. ## Common Formats The following are methods to display a `DateTime` instance as a common format: ```python >>> import pendulum >>> dt = pendulum.now() >>> dt.to_atom_string() '1975-12-25T14:15:16-05:00' >>> dt.to_cookie_string() 'Thursday, 25-Dec-1975 14:15:16 EST' >>> dt.to_iso8601_string() '1975-12-25T14:15:16-0500' >>> dt.to_rfc822_string() 'Thu, 25 Dec 75 14:15:16 -0500' >>> dt.to_rfc850_string() 'Thursday, 25-Dec-75 14:15:16 EST' >>> dt.to_rfc1036_string() 'Thu, 25 Dec 75 14:15:16 -0500' >>> dt.to_rfc1123_string() 'Thu, 25 Dec 1975 14:15:16 -0500' >>> dt.to_rfc2822_string() 'Thu, 25 Dec 1975 14:15:16 -0500' >>> dt.to_rfc3339_string() '1975-12-25T14:15:16-05:00' >>> dt.to_rss_string() 'Thu, 25 Dec 1975 14:15:16 -0500' >>> dt.to_w3c_string() '1975-12-25T14:15:16-05:00' ``` ## Formatter Pendulum uses its own formatter when using the `format()` method. This format is more intuitive to use than the one used with `strftime()` and supports more directives. ```python >>> import pendulum >>> dt = pendulum.datetime(1975, 12, 25, 14, 15, 16) >>> dt.format('YYYY-MM-DD HH:mm:ss') '1975-12-25 14:15:16' ``` ### Tokens The following tokens are currently supported: | | Token | Output | | ------------------------------ | ------------- | ------------------------------------------ | | **Year** | YYYY | 2000, 2001, 2002 ... 2012, 2013 | | | YY | 00, 01, 02 ... 12, 13 | | | Y | 2000, 2001, 2002 ... 2012, 2013 | | **Quarter** | Q | 1 2 3 4 | | | Qo | 1st 2nd 3rd 4th | | **Month** | MMMM | January, February, March ... | | | MMM | Jan, Feb, Mar ... | | | MM | 01, 02, 03 ... 11, 12 | | | M | 1, 2, 3 ... 11, 12 | | | Mo | 1st 2nd ... 11th 12th | | **Day of Year** | DDDD | 001, 002, 003 ... 364, 365 | | | DDD | 1, 2, 3 ... 4, 5 | | **Day of Month** | DD | 01, 02, 03 ... 30, 31 | | | D | 1, 2, 3 ... 30, 31 | | | Do | 1st, 2nd, 3rd ... 30th, 31st | | **Day of Week** | dddd | Monday, Tuesday, Wednesday ... | | | ddd | Mon, Tue, Wed ... | | | dd | Mo, Tu, We ... | | | d | 0, 1, 2 ... 6 | | **Days of ISO Week** | E | 1, 2, 3 ... 7 | | **Hour** | HH | 00, 01, 02 ... 23 | | | H | 0, 1, 2 ... 23 | | | hh | 01, 02, 03 ... 11, 12 | | | h | 1, 2, 3 ... 11, 12 | | **Minute** | mm | 00, 01, 02 ... 58, 59 | | | m | 0, 1, 2 ... 58, 59 | | **Second** | ss | 00, 01, 02 ... 58, 59 | | | s | 0, 1, 2 ... 58, 59 | | **Fractional Second** | S | 0 1 ... 8 9 | | | SS | 00, 01, 02 ... 98, 99 | | | SSS | 000 001 ... 998 999 | | | SSSS ... | 000[0..] 001[0..] ... 998[0..] 999[0..] | | | SSSSSS | | | **AM / PM** | A | AM, PM | | **Timezone** | Z | -07:00, -06:00 ... +06:00, +07:00 | | | ZZ | -0700, -0600 ... +0600, +0700 | | | z | Asia/Baku, Europe/Warsaw, GMT ... | | | zz | EST CST ... MST PST | | **Seconds timestamp** | X | 1381685817, 1234567890.123 | | **Milliseconds timestamp** | x | 1234567890123 | ### Localized Formats Because preferred formatting differs based on locale, there are a few tokens that can be used to format an instance based on its locale. | | | | | ------------------------------------------------------ | ------------- | ------------------------------------------ | | **Time** | LT | 8:30 PM | | **Time with seconds** | LTS | 8:30:25 PM | | **Month numeral, day of month, year** | L | 09/04/1986 | | **Month name, day of month, year** | LL | September 4 1986 | | **Month name, day of month, year, time** | LLL | September 4 1986 8:30 PM | | **Month name, day of month, day of week, year, time** | LLLL | Thursday, September 4 1986 8:30 PM | ### Escaping Characters To escape characters in format strings, you can wrap the characters in square brackets. ```python >>> import pendulum >>> dt = pendulum.now() >>> dt.format('[today] dddd') 'today Sunday' ``` pendulum-3.0.0/docs/docs/testing.md000066400000000000000000000044321453741033500172320ustar00rootroot00000000000000# Testing Pendulum provides a few helpers to help you control the flow of time in your tests. Note that these helpers are only available if you opted in the `test` extra during [installation](#installation). !!!warning If you are migrating from Pendulum 2, note that the `set_test_now()` and `test()` helpers have been removed. ## Relative time travel You can travel in time relatively to the current time ```python >>> import pendulum >>> now = pendulum.now() >>> pendulum.travel(minutes=5) >>> pendulum.now().diff_for_humans(now) "5 minutes after" ``` Note that once you've travelled in time the clock **keeps ticking**. If you prefer to stop the time completely you can use the `freeze` parameter: ```python >>> import pendulum >>> now = pendulum.now() >>> pendulum.travel(minutes=5, freeze=True) >>> pendulum.now().diff_for_humans(now) "5 minutes after" # This will stay like this indefinitely ``` ## Absolute time travel Sometimes, you may want to place yourself at a specific point in time. This is possible by using the `travel_to()` helper. This helper accepts a `DateTime` instance that represents the point in time where you want to travel to. ```python >>> import pendulum >>> pendulum.travel_to(pendulum.yesterday()) ``` Similarly to `travel`, it's important to remember that, by default, the time keeps ticking so, if you prefer stopping the time, use the `freeze` parameter: ```python >>> import pendulum >>> pendulum.travel_to(pendulum.yesterday(), freeze=True) ``` ## Travelling back to the present Using any of the travel helpers will keep you in the past, or future, until you decide to travel back to the present time. To do so, you may use the `travel_back()` helper. ```python >>> import pendulum >>> now = pendulum.now() >>> pendulum.travel(minutes=5, freeze=True) >>> pendulum.now().diff_for_humans(now) "5 minutes after" >>> pendulum.travel_back() >>> pendulum.now().diff_for_humans(now) "a few seconds after" ``` However, it might be cumbersome to remember to travel back so, instead, you can use any of the helpers as a context manager: ```python >>> import pendulum >>> now = pendulum.now() >>> with pendulum.travel(minutes=5, freeze=True): >>> pendulum.now().diff_for_humans(now) "5 minutes after" >>> pendulum.now().diff_for_humans(now) "a few seconds after" ``` pendulum-3.0.0/docs/docs/timezones.md000066400000000000000000000133301453741033500175670ustar00rootroot00000000000000# Timezones Timezones are an important part of every datetime library, and `pendulum` tries to provide an easy and accurate system to handle them properly. !!!note The timezone system works best inside the `pendulum` ecosystem but can also be used with the standard ``datetime`` library with a few limitations. See [Using the timezone library directly](#using-the-timezone-library-directly). ## Normalization When you create a `DateTime` instance, the library will normalize it for the given timezone to properly handle any transition that might have occurred. ```python >>> import pendulum >>> pendulum.datetime(2013, 3, 31, 2, 30, tz='Europe/Paris') # 2:30 for the 31th of March 2013 does not exist # so pendulum will return the actual time which is 3:30+02:00 '2013-03-31T03:30:00+02:00' >>> pendulum.datetime(2013, 10, 27, 2, 30, tz='Europe/Paris') # Here, 2:30 exists twice in the day so pendulum will # assume that the transition already occurred '2013-10-27T02:30:00+01:00' ``` You can, however, control the normalization behavior: ```python >>> import pendulum >>> pendulum.datetime(2013, 3, 31, 2, 30, 0, 0, tz='Europe/Paris', dst_rule=pendulum.PRE_TRANSITION) '2013-03-31T01:30:00+01:00' >>> pendulum.datetime(2013, 10, 27, 2, 30, 0, 0, tz='Europe/Paris', dst_rule=pendulum.PRE_TRANSITION) '2013-10-27T02:30:00+02:00' >>> pendulum.datetime(2013, 3, 31, 2, 30, 0, 0, tz='Europe/Paris', dst_rule=pendulum.TRANSITION_ERROR) # NonExistingTime: The datetime 2013-03-31 02:30:00 does not exist >>> pendulum.datetime(2013, 10, 27, 2, 30, 0, 0, tz='Europe/Paris', dst_rule=pendulum.TRANSITION_ERROR) # AmbiguousTime: The datetime 2013-10-27 02:30:00 is ambiguous. ``` Note that it only affects instances at creation time. Shifting time around transition times still behaves the same. ## Shifting time to transition So, what happens when you add time to a `DateTime` instance and stumble upon a transition time? Well `pendulum`, provided with the context of the previous instance, will adopt the proper behavior and apply the transition accordingly. ```python >>> import pendulum >>> dt = pendulum.datetime(2013, 3, 31, 1, 59, 59, 999999, tz='Europe/Paris') '2013-03-31T01:59:59.999999+01:00' >>> dt = dt.add(microseconds=1) '2013-03-31T03:00:00+02:00' >>> dt.subtract(microseconds=1) '2013-03-31T01:59:59.999999+01:00' >>> dt = pendulum.datetime(2013, 10, 27, 2, 59, 59, 999999, tz='Europe/Paris', dst_rule=pendulum.PRE_TRANSITION) '2013-10-27T02:59:59.999999+02:00' >>> dt = dt.add(microseconds=1) '2013-10-27T02:00:00+01:00' >>> dt = dt.subtract(microseconds=1) '2013-10-27T02:59:59.999999+02:00' ``` ## Switching timezones You can easily change the timezone of a `DateTime` instance with the `in_timezone()` method. !!!note You can also use the more concise ``in_tz()`` ```python >>> in_paris = pendulum.datetime(2016, 8, 7, 22, 24, 30, tz='Europe/Paris') '2016-08-07T22:24:30+02:00' >>> in_paris.in_timezone('America/New_York') '2016-08-07T16:24:30-04:00' >>> in_paris.in_tz('Asia/Tokyo') '2016-08-08T05:24:30+09:00' ``` ## Using the timezone library directly !!!warning **You should avoid using the timezone library in Python < 3.6.** This is due to the fact that Pendulum relies heavily on the presence of the `fold` attribute which was introduced in Python 3.6. The reason it works inside the Pendulum ecosystem is that it backports the `fold` attribute in the `DateTime` class. Like said in the introduction, you can use the timezone library directly with standard `datetime` objects but with limitations, especially when adding and subtracting time around transition times. The value of the `fold` attribute will be used by default to determine the transition rule. ```python >>> from datetime import datetime >>> from pendulum import timezone >>> paris = timezone('Europe/Paris') >>> dt = datetime(2013, 3, 31, 2, 30) # By default, fold is set to 0 >>> dt = paris.convert(dt) >>> dt.isoformat() '2013-03-31T01:30:00+01:00' >>> dt = datetime(2013, 3, 31, 2, 30, fold=1) >>> dt = paris.convert(dt) >>> dt.isoformat() '2013-03-31T03:30:00+02:00' ``` Instead of relying on the `fold` attribute, you can use the `dst_rule` keyword argument. This is especially useful if you want to raise errors on non-existing and ambiguous times. ```python >>> import pendulum >>> dt = datetime(2013, 3, 31, 2, 30) # By default, fold is set to 0 >>> dt = paris.convert(dt, dst_rule=pendulum.PRE_TRANSITION) >>> dt.isoformat() '2013-03-31T01:30:00+01:00' >>> dt = paris.convert(dt, dst_rule=pendulum.POST_TRANSITION) >>> dt.isoformat() '2013-03-31T03:30:00+02:00' >>> paris.convert(dt, dst_rule=pendulum.TRANSITION_ERROR) # NonExistingTime: The datetime 2013-03-31 02:30:00 does not exist ``` This works as expected. However, whenever we add or subtract a `timedelta` object, things get tricky. ```python >>> from datetime import datetime, timedelta >>> from pendulum import timezone >>> dt = datetime(2013, 3, 31, 1, 59, 59, 999999) >>> dt = paris.convert(dt) >>> dt.isoformat() '2013-03-31T01:59:59.999999+01:00' >>> dt = dt + timedelta(microseconds=1) >>> dt.isoformat() '2013-03-31T02:00:00+01:00' ``` This is not what we expect. It should be `2013-03-31T03:00:00+02:00`. It is actually easy to retrieve the proper datetime by using `convert()` again. ```python >>> dt = tz.convert(dt) >>> dt.isoformat() '2013-03-31T03:00:00+02:00' ``` You can also get a normalized `datetime` object from a `Timezone` by using the `datetime()` method: ```python >>> import pendulum >>> tz = pendulum.timezone('Europe/Paris') >>> dt = tz.datetime(2013, 3, 31, 2, 30) >>> dt.isoformat() '2013-03-31T03:30:00+02:00' ``` pendulum-3.0.0/docs/mkdocs.yml000066400000000000000000000004011453741033500162760ustar00rootroot00000000000000site_name: Pendulum documentation theme: name: null custom_dir: theme extra: version: 2.1 markdown_extensions: - codehilite - admonition - pymdownx.superfences - toc: permalink: ïƒ - markdown_include.include: base_path: docs pendulum-3.0.0/docs/theme/000077500000000000000000000000001453741033500154025ustar00rootroot00000000000000pendulum-3.0.0/docs/theme/main.html000066400000000000000000000021051453741033500172120ustar00rootroot00000000000000--- layout: documentation title: {{ config.site_name|striptags|e }} version: 2.x ---
Version {{config.extra.version}}

Documentation

{{page.content}}
pendulum-3.0.0/poetry.lock000066400000000000000000002643401453741033500155550ustar00rootroot00000000000000# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. [[package]] name = "babel" version = "2.14.0" description = "Internationalization utilities" optional = false python-versions = ">=3.7" files = [ {file = "Babel-2.14.0-py3-none-any.whl", hash = "sha256:efb1a25b7118e67ce3a259bed20545c29cb68be8ad2c784c83689981b7a57287"}, {file = "Babel-2.14.0.tar.gz", hash = "sha256:6919867db036398ba21eb5c7a0f6b28ab8cbc3ae7a73a44ebe34ae74a4e7d363"}, ] [package.dependencies] pytz = {version = ">=2015.7", markers = "python_version < \"3.9\""} [package.extras] dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] [[package]] name = "backports-zoneinfo" version = "0.2.1" description = "Backport of the standard library zoneinfo module" optional = false python-versions = ">=3.6" files = [ {file = "backports.zoneinfo-0.2.1-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:da6013fd84a690242c310d77ddb8441a559e9cb3d3d59ebac9aca1a57b2e18bc"}, {file = "backports.zoneinfo-0.2.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:89a48c0d158a3cc3f654da4c2de1ceba85263fafb861b98b59040a5086259722"}, {file = "backports.zoneinfo-0.2.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:1c5742112073a563c81f786e77514969acb58649bcdf6cdf0b4ed31a348d4546"}, {file = "backports.zoneinfo-0.2.1-cp36-cp36m-win32.whl", hash = "sha256:e8236383a20872c0cdf5a62b554b27538db7fa1bbec52429d8d106effbaeca08"}, {file = "backports.zoneinfo-0.2.1-cp36-cp36m-win_amd64.whl", hash = "sha256:8439c030a11780786a2002261569bdf362264f605dfa4d65090b64b05c9f79a7"}, {file = "backports.zoneinfo-0.2.1-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:f04e857b59d9d1ccc39ce2da1021d196e47234873820cbeaad210724b1ee28ac"}, {file = "backports.zoneinfo-0.2.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:17746bd546106fa389c51dbea67c8b7c8f0d14b5526a579ca6ccf5ed72c526cf"}, {file = "backports.zoneinfo-0.2.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5c144945a7752ca544b4b78c8c41544cdfaf9786f25fe5ffb10e838e19a27570"}, {file = "backports.zoneinfo-0.2.1-cp37-cp37m-win32.whl", hash = "sha256:e55b384612d93be96506932a786bbcde5a2db7a9e6a4bb4bffe8b733f5b9036b"}, {file = "backports.zoneinfo-0.2.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a76b38c52400b762e48131494ba26be363491ac4f9a04c1b7e92483d169f6582"}, {file = "backports.zoneinfo-0.2.1-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:8961c0f32cd0336fb8e8ead11a1f8cd99ec07145ec2931122faaac1c8f7fd987"}, {file = "backports.zoneinfo-0.2.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e81b76cace8eda1fca50e345242ba977f9be6ae3945af8d46326d776b4cf78d1"}, {file = "backports.zoneinfo-0.2.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7b0a64cda4145548fed9efc10322770f929b944ce5cee6c0dfe0c87bf4c0c8c9"}, {file = "backports.zoneinfo-0.2.1-cp38-cp38-win32.whl", hash = "sha256:1b13e654a55cd45672cb54ed12148cd33628f672548f373963b0bff67b217328"}, {file = "backports.zoneinfo-0.2.1-cp38-cp38-win_amd64.whl", hash = "sha256:4a0f800587060bf8880f954dbef70de6c11bbe59c673c3d818921f042f9954a6"}, {file = "backports.zoneinfo-0.2.1.tar.gz", hash = "sha256:fadbfe37f74051d024037f223b8e001611eac868b5c5b06144ef4d8b799862f2"}, ] [package.extras] tzdata = ["tzdata"] [[package]] name = "cachetools" version = "5.3.2" description = "Extensible memoizing collections and decorators" optional = false python-versions = ">=3.7" files = [ {file = "cachetools-5.3.2-py3-none-any.whl", hash = "sha256:861f35a13a451f94e301ce2bec7cac63e881232ccce7ed67fab9b5df4d3beaa1"}, {file = "cachetools-5.3.2.tar.gz", hash = "sha256:086ee420196f7b2ab9ca2db2520aca326318b68fe5ba8bc4d49cca91add450f2"}, ] [[package]] name = "cffi" version = "1.15.1" description = "Foreign Function Interface for Python calling C code." optional = false python-versions = "*" files = [ {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"}, {file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"}, {file = "cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914"}, {file = "cffi-1.15.1-cp27-cp27m-win32.whl", hash = "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3"}, {file = "cffi-1.15.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e"}, {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162"}, {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b"}, {file = "cffi-1.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21"}, {file = "cffi-1.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185"}, {file = "cffi-1.15.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd"}, {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc"}, {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f"}, {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e"}, {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4"}, {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01"}, {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e"}, {file = "cffi-1.15.1-cp310-cp310-win32.whl", hash = "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2"}, {file = "cffi-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d"}, {file = "cffi-1.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac"}, {file = "cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"}, {file = "cffi-1.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9"}, {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c"}, {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325"}, {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c"}, {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef"}, {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8"}, {file = "cffi-1.15.1-cp311-cp311-win32.whl", hash = "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d"}, {file = "cffi-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104"}, {file = "cffi-1.15.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7"}, {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6"}, {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d"}, {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a"}, {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405"}, {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e"}, {file = "cffi-1.15.1-cp36-cp36m-win32.whl", hash = "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf"}, {file = "cffi-1.15.1-cp36-cp36m-win_amd64.whl", hash = "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497"}, {file = "cffi-1.15.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375"}, {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e"}, {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82"}, {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b"}, {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c"}, {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426"}, {file = "cffi-1.15.1-cp37-cp37m-win32.whl", hash = "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9"}, {file = "cffi-1.15.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045"}, {file = "cffi-1.15.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3"}, {file = "cffi-1.15.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a"}, {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5"}, {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca"}, {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02"}, {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192"}, {file = "cffi-1.15.1-cp38-cp38-win32.whl", hash = "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314"}, {file = "cffi-1.15.1-cp38-cp38-win_amd64.whl", hash = "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5"}, {file = "cffi-1.15.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585"}, {file = "cffi-1.15.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"}, {file = "cffi-1.15.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415"}, {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d"}, {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984"}, {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35"}, {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27"}, {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76"}, {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3"}, {file = "cffi-1.15.1-cp39-cp39-win32.whl", hash = "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee"}, {file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"}, {file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"}, ] [package.dependencies] pycparser = "*" [[package]] name = "cfgv" version = "3.4.0" description = "Validate configuration and produce human readable error messages." optional = false python-versions = ">=3.8" files = [ {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"}, {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, ] [[package]] name = "chardet" version = "5.2.0" description = "Universal encoding detector for Python 3" optional = false python-versions = ">=3.7" files = [ {file = "chardet-5.2.0-py3-none-any.whl", hash = "sha256:e1cf59446890a00105fe7b7912492ea04b6e6f06d4b742b2c788469e34c82970"}, {file = "chardet-5.2.0.tar.gz", hash = "sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7"}, ] [[package]] name = "cleo" version = "2.1.0" description = "Cleo allows you to create beautiful and testable command-line interfaces." optional = false python-versions = ">=3.7,<4.0" files = [ {file = "cleo-2.1.0-py3-none-any.whl", hash = "sha256:4a31bd4dd45695a64ee3c4758f583f134267c2bc518d8ae9a29cf237d009b07e"}, {file = "cleo-2.1.0.tar.gz", hash = "sha256:0b2c880b5d13660a7ea651001fb4acb527696c01f15c9ee650f377aa543fd523"}, ] [package.dependencies] crashtest = ">=0.4.1,<0.5.0" rapidfuzz = ">=3.0.0,<4.0.0" [[package]] name = "click" version = "8.1.7" description = "Composable command line interface toolkit" optional = false python-versions = ">=3.7" files = [ {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, ] [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} [[package]] name = "colorama" version = "0.4.6" description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] [[package]] name = "crashtest" version = "0.4.1" description = "Manage Python errors with ease" optional = false python-versions = ">=3.7,<4.0" files = [ {file = "crashtest-0.4.1-py3-none-any.whl", hash = "sha256:8d23eac5fa660409f57472e3851dab7ac18aba459a8d19cbbba86d3d5aecd2a5"}, {file = "crashtest-0.4.1.tar.gz", hash = "sha256:80d7b1f316ebfbd429f648076d6275c877ba30ba48979de4191714a75266f0ce"}, ] [[package]] name = "distlib" version = "0.3.8" description = "Distribution utilities" optional = false python-versions = "*" files = [ {file = "distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784"}, {file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"}, ] [[package]] name = "exceptiongroup" version = "1.2.0" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" files = [ {file = "exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14"}, {file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"}, ] [package.extras] test = ["pytest (>=6)"] [[package]] name = "filelock" version = "3.13.1" description = "A platform independent file lock." optional = false python-versions = ">=3.8" files = [ {file = "filelock-3.13.1-py3-none-any.whl", hash = "sha256:57dbda9b35157b05fb3e58ee91448612eb674172fab98ee235ccb0b5bee19a1c"}, {file = "filelock-3.13.1.tar.gz", hash = "sha256:521f5f56c50f8426f5e03ad3b281b490a87ef15bc6c526f168290f0c7148d44e"}, ] [package.extras] docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.24)"] testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"] typing = ["typing-extensions (>=4.8)"] [[package]] name = "ghp-import" version = "2.1.0" description = "Copy your docs directly to the gh-pages branch." optional = false python-versions = "*" files = [ {file = "ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343"}, {file = "ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619"}, ] [package.dependencies] python-dateutil = ">=2.8.1" [package.extras] dev = ["flake8", "markdown", "twine", "wheel"] [[package]] name = "identify" version = "2.5.33" description = "File identification library for Python" optional = false python-versions = ">=3.8" files = [ {file = "identify-2.5.33-py2.py3-none-any.whl", hash = "sha256:d40ce5fcd762817627670da8a7d8d8e65f24342d14539c59488dc603bf662e34"}, {file = "identify-2.5.33.tar.gz", hash = "sha256:161558f9fe4559e1557e1bff323e8631f6a0e4837f7497767c1782832f16b62d"}, ] [package.extras] license = ["ukkonen"] [[package]] name = "importlib-metadata" version = "7.0.0" description = "Read metadata from Python packages" optional = false python-versions = ">=3.8" files = [ {file = "importlib_metadata-7.0.0-py3-none-any.whl", hash = "sha256:d97503976bb81f40a193d41ee6570868479c69d5068651eb039c40d850c59d67"}, {file = "importlib_metadata-7.0.0.tar.gz", hash = "sha256:7fc841f8b8332803464e5dc1c63a2e59121f46ca186c0e2e182e80bf8c1319f7"}, ] [package.dependencies] zipp = ">=0.5" [package.extras] docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] perf = ["ipython"] testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)", "pytest-ruff"] [[package]] name = "importlib-resources" version = "6.1.1" description = "Read resources from Python packages" optional = false python-versions = ">=3.8" files = [ {file = "importlib_resources-6.1.1-py3-none-any.whl", hash = "sha256:e8bf90d8213b486f428c9c39714b920041cb02c184686a3dee24905aaa8105d6"}, {file = "importlib_resources-6.1.1.tar.gz", hash = "sha256:3893a00122eafde6894c59914446a512f728a0c1a45f9bb9b63721b6bacf0b4a"}, ] [package.dependencies] zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} [package.extras] docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] testing = ["pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-ruff", "zipp (>=3.17)"] [[package]] name = "iniconfig" version = "2.0.0" description = "brain-dead simple config-ini parsing" optional = false python-versions = ">=3.7" files = [ {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] [[package]] name = "jinja2" version = "3.1.2" description = "A very fast and expressive template engine." optional = false python-versions = ">=3.7" files = [ {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, ] [package.dependencies] MarkupSafe = ">=2.0" [package.extras] i18n = ["Babel (>=2.7)"] [[package]] name = "markdown" version = "3.5.1" description = "Python implementation of John Gruber's Markdown." optional = false python-versions = ">=3.8" files = [ {file = "Markdown-3.5.1-py3-none-any.whl", hash = "sha256:5874b47d4ee3f0b14d764324d2c94c03ea66bee56f2d929da9f2508d65e722dc"}, {file = "Markdown-3.5.1.tar.gz", hash = "sha256:b65d7beb248dc22f2e8a31fb706d93798093c308dc1aba295aedeb9d41a813bd"}, ] [package.dependencies] importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""} [package.extras] docs = ["mdx-gh-links (>=0.2)", "mkdocs (>=1.5)", "mkdocs-gen-files", "mkdocs-literate-nav", "mkdocs-nature (>=0.6)", "mkdocs-section-index", "mkdocstrings[python]"] testing = ["coverage", "pyyaml"] [[package]] name = "markdown-include" version = "0.8.1" description = "A Python-Markdown extension which provides an 'include' function" optional = false python-versions = ">=3.7" files = [ {file = "markdown-include-0.8.1.tar.gz", hash = "sha256:1d0623e0fc2757c38d35df53752768356162284259d259c486b4ab6285cdbbe3"}, {file = "markdown_include-0.8.1-py3-none-any.whl", hash = "sha256:32f0635b9cfef46997b307e2430022852529f7a5b87c0075c504283e7cc7db53"}, ] [package.dependencies] markdown = ">=3.0" [package.extras] tests = ["pytest"] [[package]] name = "markupsafe" version = "2.1.3" description = "Safely add untrusted strings to HTML/XML markup." optional = false python-versions = ">=3.7" files = [ {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa"}, {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57"}, {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f"}, {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52"}, {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00"}, {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6"}, {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779"}, {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7"}, {file = "MarkupSafe-2.1.3-cp310-cp310-win32.whl", hash = "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431"}, {file = "MarkupSafe-2.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559"}, {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c"}, {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575"}, {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee"}, {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2"}, {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9"}, {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc"}, {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9"}, {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"}, {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"}, {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-win32.whl", hash = "sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-win_amd64.whl", hash = "sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24"}, {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4"}, {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0"}, {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee"}, {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be"}, {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e"}, {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8"}, {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3"}, {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d"}, {file = "MarkupSafe-2.1.3-cp38-cp38-win32.whl", hash = "sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5"}, {file = "MarkupSafe-2.1.3-cp38-cp38-win_amd64.whl", hash = "sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc"}, {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198"}, {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b"}, {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58"}, {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e"}, {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c"}, {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636"}, {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea"}, {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e"}, {file = "MarkupSafe-2.1.3-cp39-cp39-win32.whl", hash = "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2"}, {file = "MarkupSafe-2.1.3-cp39-cp39-win_amd64.whl", hash = "sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba"}, {file = "MarkupSafe-2.1.3.tar.gz", hash = "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad"}, ] [[package]] name = "maturin" version = "1.4.0" description = "Build and publish crates with pyo3, rust-cpython and cffi bindings as well as rust binaries as python packages" optional = false python-versions = ">=3.7" files = [ {file = "maturin-1.4.0-py3-none-linux_armv6l.whl", hash = "sha256:b84bee85620e1b7b662a7af71289f7f6c23df8269e42c0f76882676dfc9c733f"}, {file = "maturin-1.4.0-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:076970a73da7fa3648204a584cd347b899c1ea67f8124b212bccd06728e63ed9"}, {file = "maturin-1.4.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:f8eded83abdb30b2b6ae6d32c80b8192bdd8bcfec0ebfacee6ac02434aa499d6"}, {file = "maturin-1.4.0-py3-none-manylinux_2_12_i686.manylinux2010_i686.musllinux_1_1_i686.whl", hash = "sha256:ff95a4494d9e57b6e74d4d7f8a9a2ee8ed29bd7f0e61855656ad959a432c0efc"}, {file = "maturin-1.4.0-py3-none-manylinux_2_12_x86_64.manylinux2010_x86_64.musllinux_1_1_x86_64.whl", hash = "sha256:16239a7648ef17976585353e381840c18e650d352576ed9545abca407d65e534"}, {file = "maturin-1.4.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:77428c043d585f038f4b056c4d617e00a8027b49598ab6d065b8f6b9b9b8d144"}, {file = "maturin-1.4.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.musllinux_1_1_armv7l.whl", hash = "sha256:b4b2f006db1e92687c814576029157dcc2d97b5750fd35fd4f3aabb97e36444f"}, {file = "maturin-1.4.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.musllinux_1_1_ppc64le.whl", hash = "sha256:ffe4e967080ceb83c156e73a37d3974b30cad01c376a86dc39a76a0c6bccf9b0"}, {file = "maturin-1.4.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:01473dc30aed8f2cee3572b3e99e3ea75bf09c84b028bf6077f7643a189699c8"}, {file = "maturin-1.4.0-py3-none-win32.whl", hash = "sha256:e669ba5984c15e29b8545b295ba6738974180b44f47f5d9e75569a5ce6b8add5"}, {file = "maturin-1.4.0-py3-none-win_amd64.whl", hash = "sha256:e2c1b157397ef3721b9c2f3f24d9a5a60bd84322aac13b4dd0704a80448741b0"}, {file = "maturin-1.4.0-py3-none-win_arm64.whl", hash = "sha256:2979175a7eee837dc3a6931980b37ddc86b9ced54d600856668fc074ca2530ef"}, {file = "maturin-1.4.0.tar.gz", hash = "sha256:ed12e1768094a7adeafc3a74ebdb8dc2201fa64c4e7e31f14cfc70378bf93790"}, ] [package.dependencies] tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} [package.extras] patchelf = ["patchelf"] zig = ["ziglang (>=0.10.0,<0.11.0)"] [[package]] name = "mergedeep" version = "1.3.4" description = "A deep merge function for ðŸ." optional = false python-versions = ">=3.6" files = [ {file = "mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307"}, {file = "mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8"}, ] [[package]] name = "mkdocs" version = "1.5.3" description = "Project documentation with Markdown." optional = false python-versions = ">=3.7" files = [ {file = "mkdocs-1.5.3-py3-none-any.whl", hash = "sha256:3b3a78e736b31158d64dbb2f8ba29bd46a379d0c6e324c2246c3bc3d2189cfc1"}, {file = "mkdocs-1.5.3.tar.gz", hash = "sha256:eb7c99214dcb945313ba30426c2451b735992c73c2e10838f76d09e39ff4d0e2"}, ] [package.dependencies] click = ">=7.0" colorama = {version = ">=0.4", markers = "platform_system == \"Windows\""} ghp-import = ">=1.0" importlib-metadata = {version = ">=4.3", markers = "python_version < \"3.10\""} jinja2 = ">=2.11.1" markdown = ">=3.2.1" markupsafe = ">=2.0.1" mergedeep = ">=1.3.4" packaging = ">=20.5" pathspec = ">=0.11.1" platformdirs = ">=2.2.0" pyyaml = ">=5.1" pyyaml-env-tag = ">=0.1" watchdog = ">=2.0" [package.extras] i18n = ["babel (>=2.9.0)"] min-versions = ["babel (==2.9.0)", "click (==7.0)", "colorama (==0.4)", "ghp-import (==1.0)", "importlib-metadata (==4.3)", "jinja2 (==2.11.1)", "markdown (==3.2.1)", "markupsafe (==2.0.1)", "mergedeep (==1.3.4)", "packaging (==20.5)", "pathspec (==0.11.1)", "platformdirs (==2.2.0)", "pyyaml (==5.1)", "pyyaml-env-tag (==0.1)", "typing-extensions (==3.10)", "watchdog (==2.0)"] [[package]] name = "mypy" version = "1.7.1" description = "Optional static typing for Python" optional = false python-versions = ">=3.8" files = [ {file = "mypy-1.7.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:12cce78e329838d70a204293e7b29af9faa3ab14899aec397798a4b41be7f340"}, {file = "mypy-1.7.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1484b8fa2c10adf4474f016e09d7a159602f3239075c7bf9f1627f5acf40ad49"}, {file = "mypy-1.7.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31902408f4bf54108bbfb2e35369877c01c95adc6192958684473658c322c8a5"}, {file = "mypy-1.7.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f2c2521a8e4d6d769e3234350ba7b65ff5d527137cdcde13ff4d99114b0c8e7d"}, {file = "mypy-1.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:fcd2572dd4519e8a6642b733cd3a8cfc1ef94bafd0c1ceed9c94fe736cb65b6a"}, {file = "mypy-1.7.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4b901927f16224d0d143b925ce9a4e6b3a758010673eeded9b748f250cf4e8f7"}, {file = "mypy-1.7.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2f7f6985d05a4e3ce8255396df363046c28bea790e40617654e91ed580ca7c51"}, {file = "mypy-1.7.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:944bdc21ebd620eafefc090cdf83158393ec2b1391578359776c00de00e8907a"}, {file = "mypy-1.7.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9c7ac372232c928fff0645d85f273a726970c014749b924ce5710d7d89763a28"}, {file = "mypy-1.7.1-cp311-cp311-win_amd64.whl", hash = "sha256:f6efc9bd72258f89a3816e3a98c09d36f079c223aa345c659622f056b760ab42"}, {file = "mypy-1.7.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6dbdec441c60699288adf051f51a5d512b0d818526d1dcfff5a41f8cd8b4aaf1"}, {file = "mypy-1.7.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4fc3d14ee80cd22367caaaf6e014494415bf440980a3045bf5045b525680ac33"}, {file = "mypy-1.7.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c6e4464ed5f01dc44dc9821caf67b60a4e5c3b04278286a85c067010653a0eb"}, {file = "mypy-1.7.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:d9b338c19fa2412f76e17525c1b4f2c687a55b156320acb588df79f2e6fa9fea"}, {file = "mypy-1.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:204e0d6de5fd2317394a4eff62065614c4892d5a4d1a7ee55b765d7a3d9e3f82"}, {file = "mypy-1.7.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:84860e06ba363d9c0eeabd45ac0fde4b903ad7aa4f93cd8b648385a888e23200"}, {file = "mypy-1.7.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8c5091ebd294f7628eb25ea554852a52058ac81472c921150e3a61cdd68f75a7"}, {file = "mypy-1.7.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40716d1f821b89838589e5b3106ebbc23636ffdef5abc31f7cd0266db936067e"}, {file = "mypy-1.7.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5cf3f0c5ac72139797953bd50bc6c95ac13075e62dbfcc923571180bebb662e9"}, {file = "mypy-1.7.1-cp38-cp38-win_amd64.whl", hash = "sha256:78e25b2fd6cbb55ddfb8058417df193f0129cad5f4ee75d1502248e588d9e0d7"}, {file = "mypy-1.7.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:75c4d2a6effd015786c87774e04331b6da863fc3fc4e8adfc3b40aa55ab516fe"}, {file = "mypy-1.7.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2643d145af5292ee956aa0a83c2ce1038a3bdb26e033dadeb2f7066fb0c9abce"}, {file = "mypy-1.7.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75aa828610b67462ffe3057d4d8a4112105ed211596b750b53cbfe182f44777a"}, {file = "mypy-1.7.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ee5d62d28b854eb61889cde4e1dbc10fbaa5560cb39780c3995f6737f7e82120"}, {file = "mypy-1.7.1-cp39-cp39-win_amd64.whl", hash = "sha256:72cf32ce7dd3562373f78bd751f73c96cfb441de147cc2448a92c1a308bd0ca6"}, {file = "mypy-1.7.1-py3-none-any.whl", hash = "sha256:f7c5d642db47376a0cc130f0de6d055056e010debdaf0707cd2b0fc7e7ef30ea"}, {file = "mypy-1.7.1.tar.gz", hash = "sha256:fcb6d9afb1b6208b4c712af0dafdc650f518836065df0d4fb1d800f5d6773db2"}, ] [package.dependencies] mypy-extensions = ">=1.0.0" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} typing-extensions = ">=4.1.0" [package.extras] dmypy = ["psutil (>=4.0)"] install-types = ["pip"] mypyc = ["setuptools (>=50)"] reports = ["lxml"] [[package]] name = "mypy-extensions" version = "1.0.0" description = "Type system extensions for programs checked with the mypy type checker." optional = false python-versions = ">=3.5" files = [ {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, ] [[package]] name = "nodeenv" version = "1.8.0" description = "Node.js virtual environment builder" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" files = [ {file = "nodeenv-1.8.0-py2.py3-none-any.whl", hash = "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec"}, {file = "nodeenv-1.8.0.tar.gz", hash = "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2"}, ] [package.dependencies] setuptools = "*" [[package]] name = "packaging" version = "23.2" description = "Core utilities for Python packages" optional = false python-versions = ">=3.7" files = [ {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, ] [[package]] name = "pathspec" version = "0.12.1" description = "Utility library for gitignore style pattern matching of file paths." optional = false python-versions = ">=3.8" files = [ {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, ] [[package]] name = "platformdirs" version = "4.1.0" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." optional = false python-versions = ">=3.8" files = [ {file = "platformdirs-4.1.0-py3-none-any.whl", hash = "sha256:11c8f37bcca40db96d8144522d925583bdb7a31f7b0e37e3ed4318400a8e2380"}, {file = "platformdirs-4.1.0.tar.gz", hash = "sha256:906d548203468492d432bcb294d4bc2fff751bf84971fbb2c10918cc206ee420"}, ] [package.extras] docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"] test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"] [[package]] name = "pluggy" version = "1.3.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.8" files = [ {file = "pluggy-1.3.0-py3-none-any.whl", hash = "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7"}, {file = "pluggy-1.3.0.tar.gz", hash = "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12"}, ] [package.extras] dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] [[package]] name = "pre-commit" version = "3.5.0" description = "A framework for managing and maintaining multi-language pre-commit hooks." optional = false python-versions = ">=3.8" files = [ {file = "pre_commit-3.5.0-py2.py3-none-any.whl", hash = "sha256:841dc9aef25daba9a0238cd27984041fa0467b4199fc4852e27950664919f660"}, {file = "pre_commit-3.5.0.tar.gz", hash = "sha256:5804465c675b659b0862f07907f96295d490822a450c4c40e747d0b1c6ebcb32"}, ] [package.dependencies] cfgv = ">=2.0.0" identify = ">=1.0.0" nodeenv = ">=0.11.1" pyyaml = ">=5.1" virtualenv = ">=20.10.0" [[package]] name = "py-cpuinfo" version = "9.0.0" description = "Get CPU info with pure Python" optional = false python-versions = "*" files = [ {file = "py-cpuinfo-9.0.0.tar.gz", hash = "sha256:3cdbbf3fac90dc6f118bfd64384f309edeadd902d7c8fb17f02ffa1fc3f49690"}, {file = "py_cpuinfo-9.0.0-py3-none-any.whl", hash = "sha256:859625bc251f64e21f077d099d4162689c762b5d6a4c3c97553d56241c9674d5"}, ] [[package]] name = "pycparser" version = "2.21" description = "C parser in Python" optional = false python-versions = "*" files = [ {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, ] [[package]] name = "pygments" version = "2.17.2" description = "Pygments is a syntax highlighting package written in Python." optional = false python-versions = ">=3.7" files = [ {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"}, {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"}, ] [package.extras] plugins = ["importlib-metadata"] windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pymdown-extensions" version = "10.5" description = "Extension pack for Python Markdown." optional = false python-versions = ">=3.8" files = [ {file = "pymdown_extensions-10.5-py3-none-any.whl", hash = "sha256:1f0ca8bb5beff091315f793ee17683bc1390731f6ac4c5eb01e27464b80fe879"}, {file = "pymdown_extensions-10.5.tar.gz", hash = "sha256:1b60f1e462adbec5a1ed79dac91f666c9c0d241fa294de1989f29d20096cfd0b"}, ] [package.dependencies] markdown = ">=3.5" pyyaml = "*" [package.extras] extra = ["pygments (>=2.12)"] [[package]] name = "pyproject-api" version = "1.6.1" description = "API to interact with the python pyproject.toml based projects" optional = false python-versions = ">=3.8" files = [ {file = "pyproject_api-1.6.1-py3-none-any.whl", hash = "sha256:4c0116d60476b0786c88692cf4e325a9814965e2469c5998b830bba16b183675"}, {file = "pyproject_api-1.6.1.tar.gz", hash = "sha256:1817dc018adc0d1ff9ca1ed8c60e1623d5aaca40814b953af14a9cf9a5cae538"}, ] [package.dependencies] packaging = ">=23.1" tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} [package.extras] docs = ["furo (>=2023.8.19)", "sphinx (<7.2)", "sphinx-autodoc-typehints (>=1.24)"] testing = ["covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)", "setuptools (>=68.1.2)", "wheel (>=0.41.2)"] [[package]] name = "pytest" version = "7.4.3" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.7" files = [ {file = "pytest-7.4.3-py3-none-any.whl", hash = "sha256:0d009c083ea859a71b76adf7c1d502e4bc170b80a8ef002da5806527b9591fac"}, {file = "pytest-7.4.3.tar.gz", hash = "sha256:d989d136982de4e3b29dabcc838ad581c64e8ed52c11fbe86ddebd9da0818cd5"}, ] [package.dependencies] colorama = {version = "*", markers = "sys_platform == \"win32\""} exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} iniconfig = "*" packaging = "*" pluggy = ">=0.12,<2.0" tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} [package.extras] testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] [[package]] name = "pytest-benchmark" version = "4.0.0" description = "A ``pytest`` fixture for benchmarking code. It will group the tests into rounds that are calibrated to the chosen timer." optional = false python-versions = ">=3.7" files = [ {file = "pytest-benchmark-4.0.0.tar.gz", hash = "sha256:fb0785b83efe599a6a956361c0691ae1dbb5318018561af10f3e915caa0048d1"}, {file = "pytest_benchmark-4.0.0-py3-none-any.whl", hash = "sha256:fdb7db64e31c8b277dff9850d2a2556d8b60bcb0ea6524e36e28ffd7c87f71d6"}, ] [package.dependencies] py-cpuinfo = "*" pytest = ">=3.8" [package.extras] aspect = ["aspectlib"] elasticsearch = ["elasticsearch"] histogram = ["pygal", "pygaljs"] [[package]] name = "pytest-codspeed" version = "1.2.2" description = "Pytest plugin to create CodSpeed benchmarks" optional = false python-versions = ">=3.7" files = [ {file = "pytest_codspeed-1.2.2-py3-none-any.whl", hash = "sha256:b8971152556e90900ae9bb8135b268592c9f3c9450974a2468a5e17f21d59ec1"}, {file = "pytest_codspeed-1.2.2.tar.gz", hash = "sha256:c59573f571181dc6a5ff423e85e1ac5aeeda18eca89d5116ff4b0897b79c98b5"}, ] [package.dependencies] cffi = ">=1.15.1,<1.16.0" pytest = ">=3.8" [package.extras] compatibility = ["pytest-benchmarks (>=3.4.1,<3.5.0)"] dev = ["black (>=22.3.0,<22.4.0)", "flake8 (>=5.0.4,<5.1.0)", "hatchling (>=1.11.1,<1.12.0)", "isort (>=5.8.0,<5.9.0)", "mypy (>=0.961,<1.0)", "pytest (>=7.0,<8.0)", "pytest-cov (>=4.0.0,<4.1.0)", "ruff (>=0.0.100,<0.1.0)"] [[package]] name = "python-dateutil" version = "2.8.2" description = "Extensions to the standard Python datetime module" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" files = [ {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, ] [package.dependencies] six = ">=1.5" [[package]] name = "pytz" version = "2023.3.post1" description = "World timezone definitions, modern and historical" optional = false python-versions = "*" files = [ {file = "pytz-2023.3.post1-py2.py3-none-any.whl", hash = "sha256:ce42d816b81b68506614c11e8937d3aa9e41007ceb50bfdcb0749b921bf646c7"}, {file = "pytz-2023.3.post1.tar.gz", hash = "sha256:7b4fddbeb94a1eba4b557da24f19fdf9db575192544270a9101d8509f9f43d7b"}, ] [[package]] name = "pyyaml" version = "6.0.1" description = "YAML parser and emitter for Python" optional = false python-versions = ">=3.6" files = [ {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}, {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}, {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}, {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}, {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}, {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}, {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}, {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}, {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}, {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, ] [[package]] name = "pyyaml-env-tag" version = "0.1" description = "A custom YAML tag for referencing environment variables in YAML files. " optional = false python-versions = ">=3.6" files = [ {file = "pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069"}, {file = "pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb"}, ] [package.dependencies] pyyaml = "*" [[package]] name = "rapidfuzz" version = "3.5.2" description = "rapid fuzzy string matching" optional = false python-versions = ">=3.8" files = [ {file = "rapidfuzz-3.5.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1a047d6e58833919d742bbc0dfa66d1de4f79e8562ee195007d3eae96635df39"}, {file = "rapidfuzz-3.5.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:22877c027c492b7dc7e3387a576a33ed5aad891104aa90da2e0844c83c5493ef"}, {file = "rapidfuzz-3.5.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e0f448b0eacbcc416feb634e1232a48d1cbde5e60f269c84e4fb0912f7bbb001"}, {file = "rapidfuzz-3.5.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d05146497672f869baf41147d5ec1222788c70e5b8b0cfcd6e95597c75b5b96b"}, {file = "rapidfuzz-3.5.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8f2df3968738a38d2a0058b5e721753f5d3d602346a1027b0dde31b0476418f3"}, {file = "rapidfuzz-3.5.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5afc1fcf1830f9bb87d3b490ba03691081b9948a794ea851befd2643069a30c1"}, {file = "rapidfuzz-3.5.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:84be69ea65f64fa01e5c4976be9826a5aa949f037508887add42da07420d65d6"}, {file = "rapidfuzz-3.5.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8658c1045766e87e0038323aa38b4a9f49b7f366563271f973c8890a98aa24b5"}, {file = "rapidfuzz-3.5.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:852b3f93c15fce58b8dc668bd54123713bfdbbb0796ba905ea5df99cfd083132"}, {file = "rapidfuzz-3.5.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:12424a06ad9bd0cbf5f7cea1015e78d924a0034a0e75a5a7b39c0703dcd94095"}, {file = "rapidfuzz-3.5.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:b4e9ded8e80530bd7205a7a2b01802f934a4695ca9e9fbe1ce9644f5e0697864"}, {file = "rapidfuzz-3.5.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:affb8fe36157c2dc8a7bc45b6a1875eb03e2c49167a1d52789144bdcb7ab3b8c"}, {file = "rapidfuzz-3.5.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c1d33a622572d384f4c90b5f7a139328246ab5600141e90032b521c2127bd605"}, {file = "rapidfuzz-3.5.2-cp310-cp310-win32.whl", hash = "sha256:2cf9f2ed4a97b388cffd48d534452a564c2491f68f4fd5bc140306f774ceb63a"}, {file = "rapidfuzz-3.5.2-cp310-cp310-win_amd64.whl", hash = "sha256:6541ffb70097885f7302cd73e2efd77be99841103023c2f9408551f27f45f7a5"}, {file = "rapidfuzz-3.5.2-cp310-cp310-win_arm64.whl", hash = "sha256:1dd2542e5103fb8ca46500a979ae14d1609dcba11d2f9fe01e99eec03420e193"}, {file = "rapidfuzz-3.5.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:bff7d3127ebc5cd908f3a72f6517f31f5247b84666137556a8fcc5177c560939"}, {file = "rapidfuzz-3.5.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fdfdb3685b631d8efbb6d6d3d86eb631be2b408d9adafcadc11e63e3f9c96dec"}, {file = "rapidfuzz-3.5.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:97b043fe8185ec53bb3ff0e59deb89425c0fc6ece6e118939963aab473505801"}, {file = "rapidfuzz-3.5.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a4a7832737f87583f3863dc62e6f56dd4a9fefc5f04a7bdcb4c433a0f36bb1b"}, {file = "rapidfuzz-3.5.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2d876dba9a11fcf60dcf1562c5a84ef559db14c2ceb41e1ad2d93cd1dc085889"}, {file = "rapidfuzz-3.5.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fa4c0612893716bbb6595066ca9ecb517c982355abe39ba9d1f4ab834ace91ad"}, {file = "rapidfuzz-3.5.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:120316824333e376b88b284724cfd394c6ccfcb9818519eab5d58a502e5533f0"}, {file = "rapidfuzz-3.5.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9cdbe8e80cc186d55f748a34393533a052d855357d5398a1ccb71a5021b58e8d"}, {file = "rapidfuzz-3.5.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1062425c8358a547ae5ebad148f2e0f02417716a571b803b0c68e4d552e99d32"}, {file = "rapidfuzz-3.5.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:66be181965aff13301dd5f9b94b646ce39d99c7fe2fd5de1656f4ca7fafcb38c"}, {file = "rapidfuzz-3.5.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:53df7aea3cf301633cfa2b4b2c2d2441a87dfc878ef810e5b4eddcd3e68723ad"}, {file = "rapidfuzz-3.5.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:76639dca5eb0afc6424ac5f42d43d3bd342ac710e06f38a8c877d5b96de09589"}, {file = "rapidfuzz-3.5.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:27689361c747b5f7b8a26056bc60979875323f1c3dcaaa9e2fec88f03b20a365"}, {file = "rapidfuzz-3.5.2-cp311-cp311-win32.whl", hash = "sha256:99c9fc5265566fb94731dc6826f43c5109e797078264e6389a36d47814473692"}, {file = "rapidfuzz-3.5.2-cp311-cp311-win_amd64.whl", hash = "sha256:666928ee735562a909d81bd2f63207b3214afd4ca41f790ab3025d066975c814"}, {file = "rapidfuzz-3.5.2-cp311-cp311-win_arm64.whl", hash = "sha256:d55de67c48f06b7772541e8d4c062a2679205799ce904236e2836cb04c106442"}, {file = "rapidfuzz-3.5.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:04e1e02b182283c43c866e215317735e91d22f5d34e65400121c04d5ed7ed859"}, {file = "rapidfuzz-3.5.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:365e544aba3ac13acf1a62cb2e5909ad2ba078d0bfc7d69b1f801dfd673b9782"}, {file = "rapidfuzz-3.5.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b61f77d834f94b0099fa9ed35c189b7829759d4e9c2743697a130dd7ba62259f"}, {file = "rapidfuzz-3.5.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:43fb368998b9703fa8c63db292a8ab9e988bf6da0c8a635754be8e69da1e7c1d"}, {file = "rapidfuzz-3.5.2-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:25510b5d142c47786dbd27cfd9da7cae5bdea28d458379377a3644d8460a3404"}, {file = "rapidfuzz-3.5.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bf3093443751e5a419834162af358d1e31dec75f84747a91dbbc47b2c04fc085"}, {file = "rapidfuzz-3.5.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2fbaf546f15a924613f89d609ff66b85b4f4c2307ac14d93b80fe1025b713138"}, {file = "rapidfuzz-3.5.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32d580df0e130ed85400ff77e1c32d965e9bc7be29ac4072ab637f57e26d29fb"}, {file = "rapidfuzz-3.5.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:358a0fbc49343de20fee8ebdb33c7fa8f55a9ff93ff42d1ffe097d2caa248f1b"}, {file = "rapidfuzz-3.5.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:fb379ac0ddfc86c5542a225d194f76ed468b071b6f79ff57c4b72e635605ad7d"}, {file = "rapidfuzz-3.5.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7fb21e182dc6d83617e88dea002963d5cf99cf5eabbdbf04094f503d8fe8d723"}, {file = "rapidfuzz-3.5.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:c04f9f1310ce414ab00bdcbf26d0906755094bfc59402cb66a7722c6f06d70b2"}, {file = "rapidfuzz-3.5.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:f6da61cc38c1a95efc5edcedf258759e6dbab73191651a28c5719587f32a56ad"}, {file = "rapidfuzz-3.5.2-cp312-cp312-win32.whl", hash = "sha256:f823fd1977071486739f484e27092765d693da6beedaceece54edce1dfeec9b2"}, {file = "rapidfuzz-3.5.2-cp312-cp312-win_amd64.whl", hash = "sha256:a8162d81486de85ab1606e48e076431b66d44cf431b2b678e9cae458832e7147"}, {file = "rapidfuzz-3.5.2-cp312-cp312-win_arm64.whl", hash = "sha256:dfc63fabb7d8da8483ca836bae7e55766fe39c63253571e103c034ba8ea80950"}, {file = "rapidfuzz-3.5.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:df8fae2515a1e4936affccac3e7d506dd904de5ff82bc0b1433b4574a51b9bfb"}, {file = "rapidfuzz-3.5.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:dd6384780c2a16097d47588844cd677316a90e0f41ef96ff485b62d58de79dcf"}, {file = "rapidfuzz-3.5.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:467a4d730ae3bade87dba6bd769e837ab97e176968ce20591fe8f7bf819115b1"}, {file = "rapidfuzz-3.5.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54576669c1502b751b534bd76a4aeaaf838ed88b30af5d5c1b7d0a3ca5d4f7b5"}, {file = "rapidfuzz-3.5.2-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abafeb82f85a651a9d6d642a33dc021606bc459c33e250925b25d6b9e7105a2e"}, {file = "rapidfuzz-3.5.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73e14617a520c0f1bc15eb78c215383477e5ca70922ecaff1d29c63c060e04ca"}, {file = "rapidfuzz-3.5.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7cdf92116e9dfe40da17f921cdbfa0039dde9eb158914fa5f01b1e67a20b19cb"}, {file = "rapidfuzz-3.5.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1962d5ccf8602589dbf8e85246a0ee2b4050d82fade1568fb76f8a4419257704"}, {file = "rapidfuzz-3.5.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:db45028eae2fda7a24759c69ebeb2a7fbcc1a326606556448ed43ee480237a3c"}, {file = "rapidfuzz-3.5.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:b685abb8b6d97989f6c69556d7934e0e533aa8822f50b9517ff2da06a1d29f23"}, {file = "rapidfuzz-3.5.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:40139552961018216b8cd88f6df4ecbbe984f907a62a5c823ccd907132c29a14"}, {file = "rapidfuzz-3.5.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:0fef4705459842ef8f79746d6f6a0b5d2b6a61a145d7d8bbe10b2e756ea337c8"}, {file = "rapidfuzz-3.5.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6b2ad5516f7068c7d9cbcda8ac5906c589e99bc427df2e1050282ee2d8bc2d58"}, {file = "rapidfuzz-3.5.2-cp38-cp38-win32.whl", hash = "sha256:2da3a24c2f7dfca7f26ba04966b848e3bbeb93e54d899908ff88dfe3e1def9dc"}, {file = "rapidfuzz-3.5.2-cp38-cp38-win_amd64.whl", hash = "sha256:e3f2be79d4114d01f383096dbee51b57df141cb8b209c19d0cf65f23a24e75ba"}, {file = "rapidfuzz-3.5.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:089a7e96e5032821af5964d8457fcb38877cc321cdd06ad7c5d6e3d852264cb9"}, {file = "rapidfuzz-3.5.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:75d8a52bf8d1aa2ac968ae4b21b83b94fc7e5ea3dfbab34811fc60f32df505b2"}, {file = "rapidfuzz-3.5.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2bacce6bbc0362f0789253424269cc742b1f45e982430387db3abe1d0496e371"}, {file = "rapidfuzz-3.5.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e5fd627e604ddc02db2ddb9ddc4a91dd92b7a6d6378fcf30bb37b49229072b89"}, {file = "rapidfuzz-3.5.2-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b2e8b369f23f00678f6e673572209a5d3b0832f4991888e3df97af7b8b9decf3"}, {file = "rapidfuzz-3.5.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c29958265e4c2b937269e804b8a160c027ee1c2627d6152655008a8b8083630e"}, {file = "rapidfuzz-3.5.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:00be97f9219355945c46f37ac9fa447046e6f7930f7c901e5d881120d1695458"}, {file = "rapidfuzz-3.5.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ada0d8d57e0f556ef38c24fee71bfe8d0db29c678bff2acd1819fc1b74f331c2"}, {file = "rapidfuzz-3.5.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:de89585268ed8ee44e80126814cae63ff6b00d08416481f31b784570ef07ec59"}, {file = "rapidfuzz-3.5.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:908ff2de9c442b379143d1da3c886c63119d4eba22986806e2533cee603fe64b"}, {file = "rapidfuzz-3.5.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:54f0061028723c026020f5bb20649c22bc8a0d9f5363c283bdc5901d4d3bff01"}, {file = "rapidfuzz-3.5.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:b581107ec0c610cdea48b25f52030770be390db4a9a73ca58b8d70fa8a5ec32e"}, {file = "rapidfuzz-3.5.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:1d5a686ea258931aaa38019204bdc670bbe14b389a230b1363d84d6cf4b9dc38"}, {file = "rapidfuzz-3.5.2-cp39-cp39-win32.whl", hash = "sha256:97f811ca7709c6ee8c0b55830f63b3d87086f4abbcbb189b4067e1cd7014db7b"}, {file = "rapidfuzz-3.5.2-cp39-cp39-win_amd64.whl", hash = "sha256:58ee34350f8c292dd24a050186c0e18301d80da904ef572cf5fda7be6a954929"}, {file = "rapidfuzz-3.5.2-cp39-cp39-win_arm64.whl", hash = "sha256:c5075ce7b9286624cafcf36720ef1cfb2946d75430b87cb4d1f006e82cd71244"}, {file = "rapidfuzz-3.5.2-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:af5221e4f7800db3e84c46b79dba4112e3b3cc2678f808bdff4fcd2487073846"}, {file = "rapidfuzz-3.5.2-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8501d7875b176930e6ed9dbc1bc35adb37ef312f6106bd6bb5c204adb90160ac"}, {file = "rapidfuzz-3.5.2-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e414e1ca40386deda4291aa2d45062fea0fbaa14f95015738f8bb75c4d27f862"}, {file = "rapidfuzz-3.5.2-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2059cd73b7ea779a9307d7a78ed743f0e3d33b88ccdcd84569abd2953cd859f"}, {file = "rapidfuzz-3.5.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:58e3e21f6f13a7cca265cce492bc797425bd4cb2025fdd161a9e86a824ad65ce"}, {file = "rapidfuzz-3.5.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:b847a49377e64e92e11ef3d0a793de75451526c83af015bdafdd5d04de8a058a"}, {file = "rapidfuzz-3.5.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a42c7a8c62b29c4810e39da22b42524295fcb793f41c395c2cb07c126b729e83"}, {file = "rapidfuzz-3.5.2-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:51b5166be86e09e011e92d9862b1fe64c4c7b9385f443fb535024e646d890460"}, {file = "rapidfuzz-3.5.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f808dcb0088a7a496cc9895e66a7b8de55ffea0eb9b547c75dfb216dd5f76ed"}, {file = "rapidfuzz-3.5.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:d4b05a8f4ab7e7344459394094587b033fe259eea3a8720035e8ba30e79ab39b"}, {file = "rapidfuzz-3.5.2.tar.gz", hash = "sha256:9e9b395743e12c36a3167a3a9fd1b4e11d92fb0aa21ec98017ee6df639ed385e"}, ] [package.extras] full = ["numpy"] [[package]] name = "setuptools" version = "69.0.2" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ {file = "setuptools-69.0.2-py3-none-any.whl", hash = "sha256:1e8fdff6797d3865f37397be788a4e3cba233608e9b509382a2777d25ebde7f2"}, {file = "setuptools-69.0.2.tar.gz", hash = "sha256:735896e78a4742605974de002ac60562d286fa8051a7e2299445e8e8fbb01aa6"}, ] [package.extras] docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.1)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] [[package]] name = "six" version = "1.16.0" description = "Python 2 and 3 compatibility utilities" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" files = [ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, ] [[package]] name = "time-machine" version = "2.13.0" description = "Travel through time in your tests." optional = false python-versions = ">=3.8" files = [ {file = "time_machine-2.13.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:685d98593f13649ad5e7ce3e58efe689feca1badcf618ba397d3ab877ee59326"}, {file = "time_machine-2.13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ccbce292380ebf63fb9a52e6b03d91677f6a003e0c11f77473efe3913a75f289"}, {file = "time_machine-2.13.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:679cbf9b15bfde1654cf48124128d3fbe52f821fa158a98fcee5fe7e05db1917"}, {file = "time_machine-2.13.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a26bdf3462d5f12a4c1009fdbe54366c6ef22c7b6f6808705b51dedaaeba8296"}, {file = "time_machine-2.13.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dabb3b155819811b4602f7e9be936e2024e20dc99a90f103e36b45768badf9c3"}, {file = "time_machine-2.13.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0db97f92be3efe0ac62fd3f933c91a78438cef13f283b6dfc2ee11123bfd7d8a"}, {file = "time_machine-2.13.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:12eed2e9171c85b703d75c985dab2ecad4fe7025b7d2f842596fce1576238ece"}, {file = "time_machine-2.13.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bdfe4a7f033e6783c3e9a7f8d8fc0b115367330762e00a03ff35fedf663994f3"}, {file = "time_machine-2.13.0-cp310-cp310-win32.whl", hash = "sha256:3a7a0a49ce50d9c306c4343a7d6a3baa11092d4399a4af4355c615ccc321a9d3"}, {file = "time_machine-2.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:1812e48c6c58707db9988445a219a908a710ea065b2cc808d9a50636291f27d4"}, {file = "time_machine-2.13.0-cp310-cp310-win_arm64.whl", hash = "sha256:5aee23cd046abf9caeddc982113e81ba9097a01f3972e9560f5ed64e3495f66d"}, {file = "time_machine-2.13.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e9a9d150e098be3daee5c9f10859ab1bd14a61abebaed86e6d71f7f18c05b9d7"}, {file = "time_machine-2.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2bd4169b808745d219a69094b3cb86006938d45e7293249694e6b7366225a186"}, {file = "time_machine-2.13.0-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:8d526cdcaca06a496877cfe61cc6608df2c3a6fce210e076761964ebac7f77cc"}, {file = "time_machine-2.13.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cfef4ebfb4f055ce3ebc7b6c1c4d0dbfcffdca0e783ad8c6986c992915a57ed3"}, {file = "time_machine-2.13.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9f128db8997c3339f04f7f3946dd9bb2a83d15e0a40d35529774da1e9e501511"}, {file = "time_machine-2.13.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21bef5854d49b62e2c33848b5c3e8acf22a3b46af803ef6ff19529949cb7cf9f"}, {file = "time_machine-2.13.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:32b71e50b07f86916ac04bd1eefc2bd2c93706b81393748b08394509ee6585dc"}, {file = "time_machine-2.13.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1ac8ff145c63cd0dcfd9590fe694b5269aacbc130298dc7209b095d101f8cdde"}, {file = "time_machine-2.13.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:19a3b10161c91ca8e0fd79348665cca711fd2eac6ce336ff9e6b447783817f93"}, {file = "time_machine-2.13.0-cp311-cp311-win32.whl", hash = "sha256:5f87787d562e42bf1006a87eb689814105b98c4d5545874a281280d0f8b9a2d9"}, {file = "time_machine-2.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:62fd14a80b8b71726e07018628daaee0a2e00937625083f96f69ed6b8e3304c0"}, {file = "time_machine-2.13.0-cp311-cp311-win_arm64.whl", hash = "sha256:e9935aff447f5400a2665ab10ed2da972591713080e1befe1bb8954e7c0c7806"}, {file = "time_machine-2.13.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:34dcdbbd25c1e124e17fe58050452960fd16a11f9d3476aaa87260e28ecca0fd"}, {file = "time_machine-2.13.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e58d82fe0e59d6e096ada3281d647a2e7420f7da5453b433b43880e1c2e8e0c5"}, {file = "time_machine-2.13.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71acbc1febbe87532c7355eca3308c073d6e502ee4ce272b5028967847c8e063"}, {file = "time_machine-2.13.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dec0ec2135a4e2a59623e40c31d6e8a8ae73305ade2634380e4263d815855750"}, {file = "time_machine-2.13.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e3a2611f8788608ebbcb060a5e36b45911bc3b8adc421b1dc29d2c81786ce4d"}, {file = "time_machine-2.13.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:42ef5349135626ad6cd889a0a81400137e5c6928502b0817ea9e90bb10702000"}, {file = "time_machine-2.13.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:6c16d90a597a8c2d3ce22d6be2eb3e3f14786974c11b01886e51b3cf0d5edaf7"}, {file = "time_machine-2.13.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4f2ae8d0e359b216b695f1e7e7256f208c390db0480601a439c5dd1e1e4e16ce"}, {file = "time_machine-2.13.0-cp312-cp312-win32.whl", hash = "sha256:f5fa9610f7e73fff42806a2ed8b06d862aa59ce4d178a52181771d6939c3e237"}, {file = "time_machine-2.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:02b33a8c19768c94f7ffd6aa6f9f64818e88afce23250016b28583929d20fb12"}, {file = "time_machine-2.13.0-cp312-cp312-win_arm64.whl", hash = "sha256:0cc116056a8a2a917a4eec85661dfadd411e0d8faae604ef6a0e19fe5cd57ef1"}, {file = "time_machine-2.13.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:de01f33aa53da37530ad97dcd17e9affa25a8df4ab822506bb08101bab0c2673"}, {file = "time_machine-2.13.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:67fa45cd813821e4f5bec0ac0820869e8e37430b15509d3f5fad74ba34b53852"}, {file = "time_machine-2.13.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4a2d3db2c3b8e519d5ef436cd405abd33542a7b7761fb05ef5a5f782a8ce0b1"}, {file = "time_machine-2.13.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7558622a62243be866a7e7c41da48eacd82c874b015ecf67d18ebf65ca3f7436"}, {file = "time_machine-2.13.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab04cf4e56e1ee65bee2adaa26a04695e92eb1ed1ccc65fbdafd0d114399595a"}, {file = "time_machine-2.13.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b0c8f24ae611a58782773af34dd356f1f26756272c04be2be7ea73b47e5da37d"}, {file = "time_machine-2.13.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4ca20f85a973a4ca8b00cf466cd72c27ccc72372549b138fd48d7e70e5a190ab"}, {file = "time_machine-2.13.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:9fad549521c4c13bdb1e889b2855a86ec835780d534ffd8f091c2647863243be"}, {file = "time_machine-2.13.0-cp38-cp38-win32.whl", hash = "sha256:20205422fcf2caf9a7488394587df86e5b54fdb315c1152094fbb63eec4e9304"}, {file = "time_machine-2.13.0-cp38-cp38-win_amd64.whl", hash = "sha256:2dc76ee55a7d915a55960a726ceaca7b9097f67e4b4e681ef89871bcf98f00be"}, {file = "time_machine-2.13.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7693704c0f2f6b9beed912ff609781edf5fcf5d63aff30c92be4093e09d94b8e"}, {file = "time_machine-2.13.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:918f8389de29b4f41317d121f1150176fae2cdb5fa41f68b2aee0b9dc88df5c3"}, {file = "time_machine-2.13.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5fe3fda5fa73fec74278912e438fce1612a79c36fd0cc323ea3dc2d5ce629f31"}, {file = "time_machine-2.13.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5c6245db573863b335d9ca64b3230f623caf0988594ae554c0c794e7f80e3e66"}, {file = "time_machine-2.13.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e433827eccd6700a34a2ab28fd9361ff6e4d4923f718d2d1dac6d1dcd9d54da6"}, {file = "time_machine-2.13.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:924377d398b1c48e519ad86a71903f9f36117f69e68242c99fb762a2465f5ad2"}, {file = "time_machine-2.13.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:66fb3877014dca0b9286b0f06fa74062357bd23f2d9d102d10e31e0f8fa9b324"}, {file = "time_machine-2.13.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0c9829b2edfcf6b5d72a6ff330d4380f36a937088314c675531b43d3423dd8af"}, {file = "time_machine-2.13.0-cp39-cp39-win32.whl", hash = "sha256:1a22be4df364f49a507af4ac9ea38108a0105f39da3f9c60dce62d6c6ea4ccdc"}, {file = "time_machine-2.13.0-cp39-cp39-win_amd64.whl", hash = "sha256:88601de1da06c7cab3d5ed3d5c3801ef683366e769e829e96383fdab6ae2fe42"}, {file = "time_machine-2.13.0-cp39-cp39-win_arm64.whl", hash = "sha256:3c87856105dcb25b5bbff031d99f06ef4d1c8380d096222e1bc63b496b5258e6"}, {file = "time_machine-2.13.0.tar.gz", hash = "sha256:c23b2408e3adcedec84ea1131e238f0124a5bc0e491f60d1137ad7239b37c01a"}, ] [package.dependencies] python-dateutil = "*" [[package]] name = "tomli" version = "2.0.1" description = "A lil' TOML parser" optional = false python-versions = ">=3.7" files = [ {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] [[package]] name = "tox" version = "4.11.4" description = "tox is a generic virtualenv management and test command line tool" optional = false python-versions = ">=3.8" files = [ {file = "tox-4.11.4-py3-none-any.whl", hash = "sha256:2adb83d68f27116812b69aa36676a8d6a52249cb0d173649de0e7d0c2e3e7229"}, {file = "tox-4.11.4.tar.gz", hash = "sha256:73a7240778fabf305aeb05ab8ea26e575e042ab5a18d71d0ed13e343a51d6ce1"}, ] [package.dependencies] cachetools = ">=5.3.1" chardet = ">=5.2" colorama = ">=0.4.6" filelock = ">=3.12.3" packaging = ">=23.1" platformdirs = ">=3.10" pluggy = ">=1.3" pyproject-api = ">=1.6.1" tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} virtualenv = ">=20.24.3" [package.extras] docs = ["furo (>=2023.8.19)", "sphinx (>=7.2.4)", "sphinx-argparse-cli (>=1.11.1)", "sphinx-autodoc-typehints (>=1.24)", "sphinx-copybutton (>=0.5.2)", "sphinx-inline-tabs (>=2023.4.21)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] testing = ["build[virtualenv] (>=0.10)", "covdefaults (>=2.3)", "detect-test-pollution (>=1.1.1)", "devpi-process (>=1)", "diff-cover (>=7.7)", "distlib (>=0.3.7)", "flaky (>=3.7)", "hatch-vcs (>=0.3)", "hatchling (>=1.18)", "psutil (>=5.9.5)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)", "pytest-xdist (>=3.3.1)", "re-assert (>=1.1)", "time-machine (>=2.12)", "wheel (>=0.41.2)"] [[package]] name = "types-python-dateutil" version = "2.8.19.14" description = "Typing stubs for python-dateutil" optional = false python-versions = "*" files = [ {file = "types-python-dateutil-2.8.19.14.tar.gz", hash = "sha256:1f4f10ac98bb8b16ade9dbee3518d9ace017821d94b057a425b069f834737f4b"}, {file = "types_python_dateutil-2.8.19.14-py3-none-any.whl", hash = "sha256:f977b8de27787639986b4e28963263fd0e5158942b3ecef91b9335c130cb1ce9"}, ] [[package]] name = "types-pytz" version = "2023.3.1.1" description = "Typing stubs for pytz" optional = false python-versions = "*" files = [ {file = "types-pytz-2023.3.1.1.tar.gz", hash = "sha256:cc23d0192cd49c8f6bba44ee0c81e4586a8f30204970fc0894d209a6b08dab9a"}, {file = "types_pytz-2023.3.1.1-py3-none-any.whl", hash = "sha256:1999a123a3dc0e39a2ef6d19f3f8584211de9e6a77fe7a0259f04a524e90a5cf"}, ] [[package]] name = "typing-extensions" version = "4.9.0" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" files = [ {file = "typing_extensions-4.9.0-py3-none-any.whl", hash = "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd"}, {file = "typing_extensions-4.9.0.tar.gz", hash = "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783"}, ] [[package]] name = "tzdata" version = "2023.3" description = "Provider of IANA time zone data" optional = false python-versions = ">=2" files = [ {file = "tzdata-2023.3-py2.py3-none-any.whl", hash = "sha256:7e65763eef3120314099b6939b5546db7adce1e7d6f2e179e3df563c70511eda"}, {file = "tzdata-2023.3.tar.gz", hash = "sha256:11ef1e08e54acb0d4f95bdb1be05da659673de4acbd21bf9c69e94cc5e907a3a"}, ] [[package]] name = "virtualenv" version = "20.25.0" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.7" files = [ {file = "virtualenv-20.25.0-py3-none-any.whl", hash = "sha256:4238949c5ffe6876362d9c0180fc6c3a824a7b12b80604eeb8085f2ed7460de3"}, {file = "virtualenv-20.25.0.tar.gz", hash = "sha256:bf51c0d9c7dd63ea8e44086fa1e4fb1093a31e963b86959257378aef020e1f1b"}, ] [package.dependencies] distlib = ">=0.3.7,<1" filelock = ">=3.12.2,<4" platformdirs = ">=3.9.1,<5" [package.extras] docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] [[package]] name = "watchdog" version = "3.0.0" description = "Filesystem events monitoring" optional = false python-versions = ">=3.7" files = [ {file = "watchdog-3.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:336adfc6f5cc4e037d52db31194f7581ff744b67382eb6021c868322e32eef41"}, {file = "watchdog-3.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a70a8dcde91be523c35b2bf96196edc5730edb347e374c7de7cd20c43ed95397"}, {file = "watchdog-3.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:adfdeab2da79ea2f76f87eb42a3ab1966a5313e5a69a0213a3cc06ef692b0e96"}, {file = "watchdog-3.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2b57a1e730af3156d13b7fdddfc23dea6487fceca29fc75c5a868beed29177ae"}, {file = "watchdog-3.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7ade88d0d778b1b222adebcc0927428f883db07017618a5e684fd03b83342bd9"}, {file = "watchdog-3.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7e447d172af52ad204d19982739aa2346245cc5ba6f579d16dac4bfec226d2e7"}, {file = "watchdog-3.0.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:9fac43a7466eb73e64a9940ac9ed6369baa39b3bf221ae23493a9ec4d0022674"}, {file = "watchdog-3.0.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:8ae9cda41fa114e28faf86cb137d751a17ffd0316d1c34ccf2235e8a84365c7f"}, {file = "watchdog-3.0.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:25f70b4aa53bd743729c7475d7ec41093a580528b100e9a8c5b5efe8899592fc"}, {file = "watchdog-3.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4f94069eb16657d2c6faada4624c39464f65c05606af50bb7902e036e3219be3"}, {file = "watchdog-3.0.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7c5f84b5194c24dd573fa6472685b2a27cc5a17fe5f7b6fd40345378ca6812e3"}, {file = "watchdog-3.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3aa7f6a12e831ddfe78cdd4f8996af9cf334fd6346531b16cec61c3b3c0d8da0"}, {file = "watchdog-3.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:233b5817932685d39a7896b1090353fc8efc1ef99c9c054e46c8002561252fb8"}, {file = "watchdog-3.0.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:13bbbb462ee42ec3c5723e1205be8ced776f05b100e4737518c67c8325cf6100"}, {file = "watchdog-3.0.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:8f3ceecd20d71067c7fd4c9e832d4e22584318983cabc013dbf3f70ea95de346"}, {file = "watchdog-3.0.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:c9d8c8ec7efb887333cf71e328e39cffbf771d8f8f95d308ea4125bf5f90ba64"}, {file = "watchdog-3.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:0e06ab8858a76e1219e68c7573dfeba9dd1c0219476c5a44d5333b01d7e1743a"}, {file = "watchdog-3.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:d00e6be486affb5781468457b21a6cbe848c33ef43f9ea4a73b4882e5f188a44"}, {file = "watchdog-3.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:c07253088265c363d1ddf4b3cdb808d59a0468ecd017770ed716991620b8f77a"}, {file = "watchdog-3.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:5113334cf8cf0ac8cd45e1f8309a603291b614191c9add34d33075727a967709"}, {file = "watchdog-3.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:51f90f73b4697bac9c9a78394c3acbbd331ccd3655c11be1a15ae6fe289a8c83"}, {file = "watchdog-3.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:ba07e92756c97e3aca0912b5cbc4e5ad802f4557212788e72a72a47ff376950d"}, {file = "watchdog-3.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:d429c2430c93b7903914e4db9a966c7f2b068dd2ebdd2fa9b9ce094c7d459f33"}, {file = "watchdog-3.0.0-py3-none-win32.whl", hash = "sha256:3ed7c71a9dccfe838c2f0b6314ed0d9b22e77d268c67e015450a29036a81f60f"}, {file = "watchdog-3.0.0-py3-none-win_amd64.whl", hash = "sha256:4c9956d27be0bb08fc5f30d9d0179a855436e655f046d288e2bcc11adfae893c"}, {file = "watchdog-3.0.0-py3-none-win_ia64.whl", hash = "sha256:5d9f3a10e02d7371cd929b5d8f11e87d4bad890212ed3901f9b4d68767bee759"}, {file = "watchdog-3.0.0.tar.gz", hash = "sha256:4d98a320595da7a7c5a18fc48cb633c2e73cda78f93cac2ef42d42bf609a33f9"}, ] [package.extras] watchmedo = ["PyYAML (>=3.10)"] [[package]] name = "zipp" version = "3.17.0" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.8" files = [ {file = "zipp-3.17.0-py3-none-any.whl", hash = "sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31"}, {file = "zipp-3.17.0.tar.gz", hash = "sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0"}, ] [package.extras] docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy (>=0.9.1)", "pytest-ruff"] [extras] test = ["time-machine"] [metadata] lock-version = "2.0" python-versions = ">=3.8" content-hash = "8df704933b8ad4ffb92d760c1215dfe15be3227a5d65d93f3a5cac8a2035565d" pendulum-3.0.0/pyproject.toml000066400000000000000000000150441453741033500162700ustar00rootroot00000000000000[project] name = "pendulum" version = "3.0.0" description = "Python datetimes made easy" readme = "README.rst" requires-python = ">=3.8" license = { text = "MIT License" } authors = [{ name = "Sébastien Eustace", email = "sebastien@eustace.io>" }] keywords = ['datetime', 'date', 'time'] classifiers = [ "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", ] dependencies = [ "python-dateutil>=2.6", "tzdata>=2020.1", 'backports.zoneinfo>=0.2.1; python_version < "3.9"', 'time-machine>=2.6.0; implementation_name != "pypy"', 'importlib-resources>=5.9.0; python_version < "3.9"' ] [project.urls] Homepage = "https://pendulum.eustace.io" Documentation = "https://pendulum.eustace.io/docs" Repository = "https://github.com/sdispater/pendulum" [tool.poetry] name = "pendulum" version = "3.0.0b1" description = "Python datetimes made easy" authors = ["Sébastien Eustace "] license = "MIT" readme = 'README.rst' homepage = "https://pendulum.eustace.io" repository = "https://github.com/sdispater/pendulum" documentation = "https://pendulum.eustace.io/docs" keywords = ['datetime', 'date', 'time'] [tool.poetry.dependencies] python = ">=3.8" python-dateutil = ">=2.6" "backports.zoneinfo" = { version = ">=0.2.1", python = "<3.9" } time-machine = { version = ">=2.6.0", markers = "implementation_name != 'pypy'", optional = true } tzdata = ">=2020.1" importlib-resources = { version = ">=5.9.0", python = "<3.9" } [tool.poetry.group.test.dependencies] pytest = "^7.1.2" pytz = ">=2022.1" time-machine = ">=2.6.0" pytest-benchmark = "^4.0.0" [tool.poetry.group.doc.dependencies] mkdocs = "^1.0" pymdown-extensions = ">=6,<11" pygments = "^2.2" markdown-include = "^0.8.1" [tool.poetry.group.lint.dependencies] pre-commit = "^3.0.0" [tool.poetry.group.typing.dependencies] mypy = "^1.3.0" types-python-dateutil = "^2.8.19" types-pytz = ">=2022.7.1.2" [tool.poetry.group.dev.dependencies] babel = "^2.10.3" cleo = { version = "^2.0.1", python = ">=3.8,<4.0" } tox = "^4.0.0" [tool.poetry.group.benchmark.dependencies] pytest-codspeed = "^1.2.2" [tool.poetry.group.build.dependencies] maturin = ">=1.0,<2.0" [tool.poetry.extras] test = ["time-machine"] [tool.maturin] module-name = "pendulum._pendulum" [tool.ruff] fix = true unfixable = [ "ERA", # do not autoremove commented out code ] target-version = "py38" line-length = 88 extend-select = [ "B", # flake8-bugbear "C4", # flake8-comprehensions "ERA", # flake8-eradicate/eradicate "I", # isort "N", # pep8-naming "PIE", # flake8-pie "PGH", # pygrep "RUF", # ruff checks "SIM", # flake8-simplify "TCH", # flake8-type-checking "TID", # flake8-tidy-imports "UP", # pyupgrade ] ignore = [ "B904", # use 'raise ... from err' "B905", # use explicit 'strict=' parameter with 'zip()' "N818", # Exception name should be named with an Error suffix "RUF001", ] extend-exclude = [ # External to the project's coding standards: "docs/*", # Machine-generated, too many false-positives "src/pendulum/locales/*", # ruff disagrees with black when it comes to formatting "*.pyi", ] [tool.ruff.flake8-tidy-imports] ban-relative-imports = "all" [tool.ruff.isort] force-single-line = true lines-between-types = 1 lines-after-imports = 2 known-first-party = ["pendulum"] known-third-party = [ "babel", "cleo", "dateutil", "time_machine", "pytzdata", ] required-imports = ["from __future__ import annotations"] [tool.ruff.extend-per-file-ignores] "build.py" = ["I002"] "clock" = ["RUF012"] [tool.mypy] strict = true files = "src, tests" show_error_codes = true pretty = true warn_unused_ignores = true exclude = [ "^build\\.py$" ] # The following whitelist is used to allow for incremental adoption # of Mypy. Modules should be removed from this whitelist as and when # their respective type errors have been addressed. No new modules # should be added to this whitelist. [[tool.mypy.overrides]] module = [ "pendulum.mixins.default", "tests.test_parsing", "tests.date.test_add", "tests.date.test_behavior", "tests.date.test_construct", "tests.date.test_comparison", "tests.date.test_day_of_week_modifiers", "tests.date.test_diff", "tests.date.test_fluent_setters", "tests.date.test_getters", "tests.date.test_start_end_of", "tests.date.test_strings", "tests.date.test_sub", "tests.datetime.test_add", "tests.datetime.test_behavior", "tests.datetime.test_construct", "tests.datetime.test_comparison", "tests.datetime.test_create_from_timestamp", "tests.datetime.test_day_of_week_modifiers", "tests.datetime.test_diff", "tests.datetime.test_fluent_setters", "tests.datetime.test_from_format", "tests.datetime.test_getters", "tests.datetime.test_naive", "tests.datetime.test_replace", "tests.datetime.test_start_end_of", "tests.datetime.test_strings", "tests.datetime.test_sub", "tests.datetime.test_timezone", "tests.duration.test_add_sub", "tests.duration.test_arithmetic", "tests.duration.test_behavior", "tests.duration.test_construct", "tests.duration.test_in_methods", "tests.duration.test_in_words", "tests.duration.test_total_methods", "tests.formatting.test_formatter", "tests.helpers.test_local_time", "tests.localization.*", "tests.parsing.test_parsing", "tests.parsing.test_parsing_duration", "tests.parsing.test_parse_iso8601", "tests.interval.test_add_subtract", "tests.interval.test_arithmetic", "tests.interval.test_behavior", "tests.interval.test_construct", "tests.interval.test_hashing", "tests.interval.test_in_words", "tests.interval.test_range", "tests.time.test_add", "tests.time.test_behavior", "tests.time.test_comparison", "tests.time.test_construct", "tests.time.test_diff", "tests.time.test_fluent_setters", "tests.time.test_strings", "tests.time.test_sub", "tests.tz.test_helpers", "tests.tz.test_local_timezone", "tests.tz.test_timezone", "tests.tz.test_timezones", ] ignore_errors = true [tool.coverage.run] omit = [ "pendulum/locales/*", "pendulum/__version__.py,", "pendulum/_extensions/*", "pendulum/parsing/iso8601.py", "pendulum/utils/_compat.py", ] [build-system] requires = ["maturin>=1.0,<2.0"] build-backend = "maturin" pendulum-3.0.0/rust/000077500000000000000000000000001453741033500143455ustar00rootroot00000000000000pendulum-3.0.0/rust/.cargo/000077500000000000000000000000001453741033500155165ustar00rootroot00000000000000pendulum-3.0.0/rust/.cargo/config.toml000066400000000000000000000004701453741033500176610ustar00rootroot00000000000000[build] rustflags = [] # see https://pyo3.rs/main/building_and_distribution.html#macos [target.x86_64-apple-darwin] rustflags = [ "-C", "link-arg=-undefined", "-C", "link-arg=dynamic_lookup", ] [target.aarch64-apple-darwin] rustflags = [ "-C", "link-arg=-undefined", "-C", "link-arg=dynamic_lookup", ] pendulum-3.0.0/rust/Cargo.lock000066400000000000000000000205701453741033500162560ustar00rootroot00000000000000# This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "_pendulum" version = "3.0.0-beta-1" dependencies = [ "mimalloc", "pyo3", ] [[package]] name = "autocfg" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "cc" version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "indoc" version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa799dd5ed20a7e349f3b4639aa80d74549c81716d9ec4f994c9b5815598306" [[package]] name = "libc" version = "0.2.139" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" [[package]] name = "libmimalloc-sys" version = "0.1.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3979b5c37ece694f1f5e51e7ecc871fdb0f517ed04ee45f88d15d6d553cb9664" dependencies = [ "cc", "libc", ] [[package]] name = "lock_api" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" dependencies = [ "autocfg", "scopeguard", ] [[package]] name = "memoffset" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" dependencies = [ "autocfg", ] [[package]] name = "mimalloc" version = "0.1.39" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa01922b5ea280a911e323e4d2fd24b7fe5cc4042e0d2cda3c40775cdc4bdc9c" dependencies = [ "libmimalloc-sys", ] [[package]] name = "once_cell" version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" [[package]] name = "parking_lot" version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ "lock_api", "parking_lot_core", ] [[package]] name = "parking_lot_core" version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", "windows-sys", ] [[package]] name = "proc-macro2" version = "1.0.51" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" dependencies = [ "unicode-ident", ] [[package]] name = "pyo3" version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffb88ae05f306b4bfcde40ac4a51dc0b05936a9207a4b75b798c7729c4258a59" dependencies = [ "cfg-if", "indoc", "libc", "memoffset", "parking_lot", "pyo3-build-config", "pyo3-ffi", "pyo3-macros", "unindent", ] [[package]] name = "pyo3-build-config" version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "554db24f0b3c180a9c0b1268f91287ab3f17c162e15b54caaae5a6b3773396b0" dependencies = [ "once_cell", "python3-dll-a", "target-lexicon", ] [[package]] name = "pyo3-ffi" version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "922ede8759e8600ad4da3195ae41259654b9c55da4f7eec84a0ccc7d067a70a4" dependencies = [ "libc", "pyo3-build-config", ] [[package]] name = "pyo3-macros" version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a5caec6a1dd355964a841fcbeeb1b89fe4146c87295573f94228911af3cc5a2" dependencies = [ "proc-macro2", "pyo3-macros-backend", "quote", "syn", ] [[package]] name = "pyo3-macros-backend" version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0b78ccbb160db1556cdb6fd96c50334c5d4ec44dc5e0a968d0a1208fa0efa8b" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "python3-dll-a" version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5f07cd4412be8fa09a721d40007c483981bbe072cd6a21f2e83e04ec8f8343f" dependencies = [ "cc", ] [[package]] name = "quote" version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" dependencies = [ "proc-macro2", ] [[package]] name = "redox_syscall" version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ "bitflags", ] [[package]] name = "scopeguard" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "smallvec" version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" [[package]] name = "syn" version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "target-lexicon" version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ae9980cab1db3fceee2f6c6f643d5d8de2997c58ee8d25fb0cc8a9e9e7348e5" [[package]] name = "unicode-ident" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" [[package]] name = "unindent" version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1766d682d402817b5ac4490b3c3002d91dfa0d22812f341609f97b08757359c" [[package]] name = "windows-sys" version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ "windows-targets", ] [[package]] name = "windows-targets" version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" [[package]] name = "windows_aarch64_msvc" version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" [[package]] name = "windows_i686_gnu" version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" [[package]] name = "windows_i686_msvc" version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" [[package]] name = "windows_x86_64_gnu" version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" [[package]] name = "windows_x86_64_gnullvm" version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" [[package]] name = "windows_x86_64_msvc" version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" pendulum-3.0.0/rust/Cargo.toml000066400000000000000000000007231453741033500162770ustar00rootroot00000000000000[package] name = "_pendulum" version = "3.0.0" edition = "2021" [lib] name = "_pendulum" crate-type = ["cdylib", "rlib"] [profile.release] lto = "fat" codegen-units = 1 strip = true overflow-checks = false [dependencies] pyo3 = { version = "0.19.0", features = ["extension-module", "generate-import-lib"] } mimalloc = { version = "0.1.39", optional = true, default-features = false } [features] extension-module = ["pyo3/extension-module"] default = ["mimalloc"] pendulum-3.0.0/rust/src/000077500000000000000000000000001453741033500151345ustar00rootroot00000000000000pendulum-3.0.0/rust/src/constants.rs000066400000000000000000000040251453741033500175170ustar00rootroot00000000000000pub const EPOCH_YEAR: u32 = 1970; pub const DAYS_PER_N_YEAR: u32 = 365; pub const DAYS_PER_L_YEAR: u32 = 366; pub const SECS_PER_MIN: u32 = 60; pub const SECS_PER_HOUR: u32 = SECS_PER_MIN * 60; pub const SECS_PER_DAY: u32 = SECS_PER_HOUR * 24; // 400-year chunks always have 146097 days (20871 weeks). pub const DAYS_PER_400_YEARS: u32 = 146_097; pub const SECS_PER_400_YEARS: u64 = DAYS_PER_400_YEARS as u64 * SECS_PER_DAY as u64; // The number of seconds in an aligned 100-year chunk, for those that // do not begin with a leap year and those that do respectively. pub const SECS_PER_100_YEARS: [u64; 2] = [ (76 * DAYS_PER_N_YEAR as u64 + 24 * DAYS_PER_L_YEAR as u64) * SECS_PER_DAY as u64, (75 * DAYS_PER_N_YEAR as u64 + 25 * DAYS_PER_L_YEAR as u64) * SECS_PER_DAY as u64, ]; // The number of seconds in an aligned 4-year chunk, for those that // do not begin with a leap year and those that do respectively. #[allow(clippy::erasing_op)] pub const SECS_PER_4_YEARS: [u32; 2] = [ (4 * DAYS_PER_N_YEAR + 0 * DAYS_PER_L_YEAR) * SECS_PER_DAY, (3 * DAYS_PER_N_YEAR + DAYS_PER_L_YEAR) * SECS_PER_DAY, ]; // The number of seconds in non-leap and leap years respectively. pub const SECS_PER_YEAR: [u32; 2] = [ DAYS_PER_N_YEAR * SECS_PER_DAY, DAYS_PER_L_YEAR * SECS_PER_DAY, ]; // The month lengths in non-leap and leap years respectively. pub const DAYS_PER_MONTHS: [[i32; 13]; 2] = [ [-1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31], [-1, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31], ]; // The day offsets of the beginning of each (1-based) month in non-leap // and leap years respectively. // For example, in a leap year there are 335 days before December. pub const MONTHS_OFFSETS: [[i32; 14]; 2] = [ [ -1, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365, ], [ -1, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366, ], ]; pub const DAY_OF_WEEK_TABLE: [u32; 12] = [0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4]; pub const TM_JANUARY: usize = 0; pub const TM_DECEMBER: usize = 11; pendulum-3.0.0/rust/src/helpers.rs000066400000000000000000000070541453741033500171520ustar00rootroot00000000000000use crate::constants::{ DAYS_PER_L_YEAR, DAYS_PER_N_YEAR, DAY_OF_WEEK_TABLE, EPOCH_YEAR, MONTHS_OFFSETS, SECS_PER_100_YEARS, SECS_PER_400_YEARS, SECS_PER_4_YEARS, SECS_PER_DAY, SECS_PER_HOUR, SECS_PER_MIN, SECS_PER_YEAR, TM_DECEMBER, TM_JANUARY, }; fn p(year: i32) -> i32 { year + year / 4 - year / 100 + year / 400 } pub fn is_leap(year: i32) -> bool { year % 4 == 0 && (year % 100 != 0 || year % 400 == 0) } pub fn is_long_year(year: i32) -> bool { (p(year) % 7 == 4) || (p(year - 1) % 7 == 3) } pub fn days_in_year(year: i32) -> u32 { if is_leap(year) { return DAYS_PER_L_YEAR; } DAYS_PER_N_YEAR } pub fn week_day(year: i32, month: u32, day: u32) -> u32 { let y: i32 = year - i32::from(month < 3); let w: i32 = (p(y) + DAY_OF_WEEK_TABLE[(month - 1) as usize] as i32 + day as i32) % 7; if w == 0 { return 7; } w.unsigned_abs() } pub fn day_number(year: i32, month: u8, day: u8) -> i32 { let m = i32::from((month + 9) % 12); let y = year - m / 10; 365 * y + y / 4 - y / 100 + y / 400 + (m * 306 + 5) / 10 + (i32::from(day) - 1) } pub fn local_time( unix_time: f64, utc_offset: isize, microsecond: usize, ) -> (usize, usize, usize, usize, usize, usize, usize) { let mut year: usize = EPOCH_YEAR as usize; let mut seconds: isize = unix_time.floor() as isize; // Shift to a base year that is 400-year aligned. if seconds >= 0 { seconds -= (10957 * SECS_PER_DAY as usize) as isize; year += 30; // == 2000 } else { seconds += ((146_097 - 10957) * SECS_PER_DAY as usize) as isize; year -= 370; // == 1600 } seconds += utc_offset; // Handle years in chunks of 400/100/4/1 year += 400 * (seconds / SECS_PER_400_YEARS as isize) as usize; seconds %= SECS_PER_400_YEARS as isize; if seconds < 0 { seconds += SECS_PER_400_YEARS as isize; year -= 400; } let mut leap_year = 1; // 4-century aligned let mut sec_per_100years = SECS_PER_100_YEARS[leap_year] as isize; while seconds >= sec_per_100years { seconds -= sec_per_100years; year += 100; leap_year = 0; // 1-century, non 4-century aligned sec_per_100years = SECS_PER_100_YEARS[leap_year] as isize; } let mut sec_per_4years = SECS_PER_4_YEARS[leap_year] as isize; while seconds >= sec_per_4years { seconds -= sec_per_4years; year += 4; leap_year = 1; // 4-year, non century aligned sec_per_4years = SECS_PER_4_YEARS[leap_year] as isize; } let mut sec_per_year = SECS_PER_YEAR[leap_year] as isize; while seconds >= sec_per_year { seconds -= sec_per_year; year += 1; leap_year = 0; // non 4-year aligned sec_per_year = SECS_PER_YEAR[leap_year] as isize; } // Handle months and days let mut month = TM_DECEMBER + 1; let mut day: usize = (seconds / (SECS_PER_DAY as isize) + 1) as usize; seconds %= SECS_PER_DAY as isize; let mut month_offset: usize; while month != (TM_JANUARY + 1) { month_offset = MONTHS_OFFSETS[leap_year][month] as usize; if day > month_offset { day -= month_offset; break; } month -= 1; } // Handle hours, minutes and seconds let hour: usize = (seconds / SECS_PER_HOUR as isize) as usize; seconds %= SECS_PER_HOUR as isize; let minute: usize = (seconds / SECS_PER_MIN as isize) as usize; let second: usize = (seconds % SECS_PER_MIN as isize) as usize; (year, month, day, hour, minute, second, microsecond) } pendulum-3.0.0/rust/src/lib.rs000066400000000000000000000003171453741033500162510ustar00rootroot00000000000000extern crate core; #[cfg(feature = "mimalloc")] #[global_allocator] static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc; mod constants; mod helpers; mod parsing; mod python; pub use python::_pendulum; pendulum-3.0.0/rust/src/parsing.rs000066400000000000000000000765251453741033500171640ustar00rootroot00000000000000use core::str; use std::{fmt, str::CharIndices}; use crate::{ constants::MONTHS_OFFSETS, helpers::{days_in_year, is_leap, is_long_year, week_day}, }; #[derive(Debug, Clone)] pub struct ParseError { index: usize, message: String, } impl fmt::Display for ParseError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{} (Position: {})", self.message, self.index) } } pub struct ParsedDateTime { pub year: u32, pub month: u32, pub day: u32, pub hour: u32, pub minute: u32, pub second: u32, pub microsecond: u32, pub offset: Option, pub has_offset: bool, pub tzname: Option, pub has_date: bool, pub has_time: bool, pub extended_date_format: bool, pub time_is_midnight: bool, } impl ParsedDateTime { pub fn new() -> ParsedDateTime { ParsedDateTime { year: 0, month: 1, day: 1, hour: 0, minute: 0, second: 0, microsecond: 0, offset: None, has_offset: false, tzname: None, has_date: false, has_time: false, extended_date_format: false, time_is_midnight: false, } } } pub struct ParsedDuration { pub years: u32, pub months: u32, pub weeks: u32, pub days: u32, pub hours: u32, pub minutes: u32, pub seconds: u32, pub microseconds: u32, } impl ParsedDuration { pub fn new() -> ParsedDuration { ParsedDuration { years: 0, months: 0, weeks: 0, days: 0, hours: 0, minutes: 0, seconds: 0, microseconds: 0, } } } pub struct Parsed { pub datetime: Option, pub duration: Option, pub second_datetime: Option, } impl Parsed { pub fn new() -> Parsed { Parsed { datetime: None, duration: None, second_datetime: None, } } } pub struct Parser<'a> { /// Input to parse. src: &'a str, /// Iterator used for getting characters from `src`. chars: CharIndices<'a>, /// Current byte offset into `src`. idx: usize, /// Current character current: char, } impl<'a> Parser<'a> { /// Creates a new parser from a &str. pub fn new(input: &'a str) -> Parser<'a> { let mut p = Parser { src: input, chars: input.char_indices(), idx: 0, current: '\0', }; p.inc(); p } /// Increments the parser if the end of the input has not been reached. /// Returns whether or not it was able to advance. fn inc(&mut self) -> Option { if let Some((i, ch)) = self.chars.next() { self.idx = i; self.current = ch; Some(ch) } else { self.idx = self.src.len(); self.current = '\0'; None } } fn parse_error(&mut self, message: String) -> ParseError { ParseError { index: self.idx, message, } } fn unexpected_character_error( &mut self, field_name: &str, expected_character_count: usize, ) -> ParseError { if self.end() { return self.parse_error(format!( "Unexpected end of string while parsing {}. Expected {} more character{}.", field_name, expected_character_count, if expected_character_count == 1 { "" } else { "s" } )); } self.parse_error(format!( "Invalid character while parsing {}: {}.", field_name, self.current, )) } /// Returns true if the parser has reached the end of the input. fn end(&self) -> bool { self.idx >= self.src.len() } fn parse_integer(&mut self, length: usize, field_name: &str) -> Result { let mut value: u32 = 0; for i in 0..length { if self.end() { return Err(self.parse_error(format!( "Unexpected end of string while parsing \"{}\". Expected {} more character{}", field_name, length - i, if (length - i) != 1 { "s" } else { "" } ))); } if let Some(digit) = self.current.to_digit(10) { value = 10 * value + digit; self.inc(); } else { return Err(self.unexpected_character_error(field_name, length - i)); } } Ok(value) } pub fn parse(&mut self) -> Result { let mut parsed = Parsed::new(); if self.current == 'P' { // Duration (and possibly time interval) self.parse_duration(&mut parsed)?; } else { self.parse_datetime(&mut parsed)?; } Ok(parsed) } fn parse_datetime(&mut self, parsed: &mut Parsed) -> Result<(), ParseError> { let mut datetime = ParsedDateTime::new(); if self.current == 'T' { self.parse_time(&mut datetime, false)?; if !self.end() { return Err(self.parse_error("Unconverted data remains".to_string())); } match &parsed.datetime { Some(_) => { parsed.second_datetime = Some(datetime); } None => match &parsed.duration { Some(_) => { parsed.second_datetime = Some(datetime); } None => { parsed.datetime = Some(datetime); } }, } return Ok(()); } datetime.year = self.parse_integer(2, "year")?; if self.current == ':' { // Time in extended format datetime.hour = datetime.year; datetime.year = 0; datetime.extended_date_format = true; self.parse_time(&mut datetime, true)?; if !self.end() { return Err(self.parse_error("Unconverted data remains".to_string())); } match &parsed.datetime { Some(_) => { parsed.second_datetime = Some(datetime); } None => match &parsed.duration { Some(_) => { parsed.second_datetime = Some(datetime); } None => { parsed.datetime = Some(datetime); } }, } return Ok(()); } datetime.has_date = true; datetime.year = datetime.year * 100 + self.parse_integer(2, "year")?; if self.current == '-' { self.inc(); datetime.extended_date_format = true; if self.current == 'W' { // ISO week and day in extended format (i.e. Www-D) self.inc(); let iso_week = self.parse_integer(2, "iso week")?; let mut iso_day: u32 = 1; if !self.end() && self.current != ' ' && self.current != 'T' { // Optional day if self.current != '-' { return Err(self.parse_error(format!( "Invalid character \"{}\" while parsing {}", self.current, "date separator" ))); } self.inc(); iso_day = self.parse_integer(1, "iso day")?; } let (year, month, day) = self.iso_to_ymd(datetime.year, iso_week, iso_day)?; datetime.year = year; datetime.month = month; datetime.day = day; } else { /* Month and day in extended format (MM-DD) or ordinal date (DDD) We'll assume first that the next part is a month and adjust if not. */ datetime.month = self.parse_integer(2, "month")?; if !self.end() && self.current != ' ' && self.current != 'T' { if self.current == '-' { // Optional day self.inc(); datetime.day = self.parse_integer(2, "day")?; } else { // Ordinal day let ordinal_day = (datetime.month * 10 + self.parse_integer(1, "ordinal day")?) as i32; let (year, month, day) = self.ordinal_to_ymd(datetime.year, ordinal_day, false)?; datetime.year = year; datetime.month = month; datetime.day = day; } } else { datetime.day = 1; } } } else if self.current == 'W' { // Compact ISO week and day (WwwD) self.inc(); let iso_week = self.parse_integer(2, "iso week")?; let mut iso_day: u32 = 1; if !self.end() && self.current != ' ' && self.current != 'T' { iso_day = self.parse_integer(1, "iso day")?; } match self.iso_to_ymd(datetime.year, iso_week, iso_day) { Ok((year, month, day)) => { datetime.year = year; datetime.month = month; datetime.day = day; } Err(error) => return Err(error), } } else { /* Month and day in compact format (MMDD) or ordinal date (DDD) We'll assume first that the next part is a month and adjust if not. */ datetime.month = self.parse_integer(2, "month")?; let mut ordinal_day = self.parse_integer(1, "ordinal day")? as i32; if self.end() || self.current == ' ' || self.current == 'T' { // Ordinal day ordinal_day += datetime.month as i32 * 10; let (year, month, day) = self.ordinal_to_ymd(datetime.year, ordinal_day, false)?; datetime.year = year; datetime.month = month; datetime.day = day; } else { // Day datetime.day = ordinal_day as u32 * 10 + self.parse_integer(1, "day")?; } } if !self.end() { self.parse_time(&mut datetime, false)?; } if !self.end() { if self.current == '/' && parsed.datetime.is_none() && parsed.duration.is_none() { // Interval parsed.datetime = Some(datetime); self.inc(); if self.current == 'P' { // Duration self.parse_duration(parsed)?; } else { self.parse_datetime(parsed)?; } return Ok(()); } return Err(self.parse_error("Unconverted data remains".to_string())); } match &parsed.datetime { Some(_) => { parsed.second_datetime = Some(datetime); } None => match &parsed.duration { Some(_) => { parsed.second_datetime = Some(datetime); } None => { parsed.datetime = Some(datetime); } }, } Ok(()) } fn parse_time( &mut self, datetime: &mut ParsedDateTime, skip_hour: bool, ) -> Result<(), ParseError> { // TODO: Add support for decimal units // Date/Time separator if self.current != 'T' && self.current != ' ' && !skip_hour { return Err(self.parse_error(format!( "Invalid character \"{}\" while parsing {}", self.current, "date and time separator (\"T\" or \" \")" ))); } datetime.has_time = true; if !skip_hour { self.inc(); // Hour datetime.hour = self.parse_integer(2, "hour")?; } if !self.end() && self.current != 'Z' && self.current != '+' && self.current != '-' { // Optional minute and second if self.current == ':' { // Minute and second in extended format (mm:ss) self.inc(); // Minute datetime.minute = self.parse_integer(2, "minute")?; if !self.end() && self.current != 'Z' && self.current != '+' && self.current != '-' { // Optional second if self.current != ':' { return Err(self.parse_error(format!( "Invalid character \"{}\" while parsing {}", self.current, "time separator (\":\")" ))); } self.inc(); // Second datetime.second = self.parse_integer(2, "second")?; if self.current == '.' || self.current == ',' { // Optional fractional second self.inc(); datetime.microsecond = 0; let mut i: u8 = 0; while i < 6 { if let Some(digit) = self.current.to_digit(10) { datetime.microsecond = datetime.microsecond * 10 + digit; } else if i == 0 { // One digit minimum is required return Err(self.unexpected_character_error("subsecond", 1)); } else { break; } self.inc(); i += 1; } // Drop extraneous digits while self.current.is_ascii_digit() { self.inc(); } // Expand missing microsecond while i < 6 { datetime.microsecond *= 10; i += 1; } } if !datetime.extended_date_format { return Err(self.parse_error("Cannot combine \"basic\" date format with \"extended\" time format (Should be either `YYYY-MM-DDThh:mm:ss` or `YYYYMMDDThhmmss`).".to_string())); } } } else { // Minute and second in compact format (mmss) // Minute datetime.minute = self.parse_integer(2, "minute")?; if !self.end() && self.current != 'Z' && self.current != '+' && self.current != '-' { // Optional second datetime.second = self.parse_integer(2, "second")?; if self.current == '.' || self.current == ',' { // Optional fractional second self.inc(); datetime.microsecond = 0; let mut i: u8 = 0; while i < 6 { if let Some(digit) = self.current.to_digit(10) { datetime.microsecond = datetime.microsecond * 10 + digit; } else if i == 0 { // One digit minimum is required return Err(self.unexpected_character_error("subsecond", 1)); } else { break; } self.inc(); i += 1; } // Drop extraneous digits while self.current.is_ascii_digit() { self.inc(); } // Expand missing microsecond while i < 6 { datetime.microsecond *= 10; i += 1; } } } if datetime.extended_date_format { return Err(self.parse_error("Cannot combine \"extended\" date format with \"basic\" time format (Should be either `YYYY-MM-DDThh:mm:ss` or `YYYYMMDDThhmmss`).".to_string())); } } } if datetime.hour == 24 && datetime.minute == 0 && datetime.second == 0 && datetime.microsecond == 0 { // Special case for 24:00:00, which is valid for ISO 8601. // This is equivalent to 00:00:00 the next day. // We will store the information for now. datetime.time_is_midnight = true; } if self.current == 'Z' { // UTC datetime.offset = Some(0); datetime.tzname = Some("UTC".to_string()); self.inc(); } else if matches!(self.current, '+' | '-') { // Optional timezone offset let tzsign = if self.current == '+' { 1 } else { -1 }; self.inc(); // Offset hour let tzhour = self.parse_integer(2, "timezone hour")? as i32; if self.current == ':' { // Optional separator self.inc(); } let mut tzminute = if self.end() { 0 } else { // Optional minute self.parse_integer(2, "timezone minute")? as i32 }; tzminute += tzhour * 60; tzminute *= tzsign; if tzminute > 24 * 60 { return Err(self.parse_error("Timezone offset is too large".to_string())); } datetime.offset = Some(tzminute * 60); } Ok(()) } fn parse_duration(&mut self, parsed: &mut Parsed) -> Result<(), ParseError> { // Removing P operator self.inc(); let mut duration: ParsedDuration = ParsedDuration::new(); let mut got_t: bool = false; let mut last_had_fraction = false; loop { match self.current { 'T' => { if got_t { return Err( self.parse_error("Repeated time declaration in duration".to_string()) ); } got_t = true; } _c => { let (value, op_fraction) = self.parse_duration_number_frac()?; if last_had_fraction { return Err(self.parse_error("Invalid duration fraction".to_string())); } if op_fraction.is_some() { last_had_fraction = true; } if got_t { match self.current { 'H' => { if duration.minutes != 0 || duration.seconds != 0 || duration.microseconds != 0 { return Err( self.parse_error("Duration units out of order".to_string()) ); } duration.hours += value; if let Some(fraction) = op_fraction { let extra_minutes = fraction * 60_f64; let extra_full_minutes: f64 = extra_minutes.trunc(); duration.minutes += extra_full_minutes as u32; let extra_seconds = ((extra_minutes - extra_full_minutes) * 60.0).round(); let extra_full_seconds = extra_seconds.trunc(); duration.seconds += extra_full_seconds as u32; let micro_extra = ((extra_seconds - extra_full_seconds) * 1_000_000.0) .round() as u32; duration.microseconds += micro_extra; } } 'M' => { if duration.seconds != 0 || duration.microseconds != 0 { return Err( self.parse_error("Duration units out of order".to_string()) ); } duration.minutes += value; if let Some(fraction) = op_fraction { let extra_seconds = fraction * 60_f64; let extra_full_seconds = extra_seconds.trunc(); duration.seconds += extra_full_seconds as u32; let micro_extra = ((extra_seconds - extra_full_seconds) * 1_000_000.0) .round() as u32; duration.microseconds += micro_extra; } } 'S' => { duration.seconds = value; if let Some(fraction) = op_fraction { duration.microseconds += (fraction * 1_000_000.0).round() as u32; } } _ => { return Err( self.parse_error("Invalid duration time unit".to_string()) ) } } } else { match self.current { 'Y' => { if last_had_fraction { return Err(self.parse_error( "Fractional years in duration are not supported" .to_string(), )); } if duration.months != 0 || duration.days != 0 { return Err( self.parse_error("Duration units out of order".to_string()) ); } duration.years = value; } 'M' => { if last_had_fraction { return Err(self.parse_error( "Fractional months in duration are not supported" .to_string(), )); } if duration.days != 0 { return Err( self.parse_error("Duration units out of order".to_string()) ); } duration.months = value; } 'W' => { if duration.years != 0 || duration.months != 0 { return Err(self.parse_error( "Basic format durations cannot have weeks".to_string(), )); } duration.weeks = value; if let Some(fraction) = op_fraction { let extra_days = fraction * 7_f64; let extra_full_days = extra_days.trunc(); duration.days += extra_full_days as u32; let extra_hours = (extra_days - extra_full_days) * 24.0; let extra_full_hours = extra_hours.trunc(); duration.hours += extra_full_hours as u32; let extra_minutes = ((extra_hours - extra_full_hours) * 60.0).round(); let extra_full_minutes: f64 = extra_minutes.trunc(); duration.minutes += extra_full_minutes as u32; let extra_seconds = ((extra_minutes - extra_full_minutes) * 60.0).round(); let extra_full_seconds = extra_seconds.trunc(); duration.seconds += extra_full_seconds as u32; let micro_extra = ((extra_seconds - extra_full_seconds) * 1_000_000.0) .round() as u32; duration.microseconds += micro_extra; } } 'D' => { if duration.weeks != 0 { return Err(self.parse_error( "Week format durations cannot have days".to_string(), )); } duration.days += value; if let Some(fraction) = op_fraction { let extra_hours = fraction * 24.0; let extra_full_hours = extra_hours.trunc(); duration.hours += extra_full_hours as u32; let extra_minutes = ((extra_hours - extra_full_hours) * 60.0).round(); let extra_full_minutes: f64 = extra_minutes.trunc(); duration.minutes += extra_full_minutes as u32; let extra_seconds = ((extra_minutes - extra_full_minutes) * 60.0).round(); let extra_full_seconds = extra_seconds.trunc(); duration.seconds += extra_full_seconds as u32; let micro_extra = ((extra_seconds - extra_full_seconds) * 1_000_000.0) .round() as u32; duration.microseconds += micro_extra; } } _ => { return Err( self.parse_error("Invalid duration time unit".to_string()) ) } } } } } self.inc(); if self.end() { break; } } parsed.duration = Some(duration); Ok(()) } fn parse_duration_number_frac(&mut self) -> Result<(u32, Option), ParseError> { let value = self.parse_duration_number()?; let fraction = matches!(self.current, '.' | ',').then(|| { let mut decimal = 0_f64; let mut denominator = 1_f64; while let Some(digit) = self.inc().and_then(|ch| ch.to_digit(10)) { decimal *= 10.0; decimal += f64::from(digit); denominator *= 10.0; } decimal / denominator }); Ok((value, fraction)) } fn parse_duration_number(&mut self) -> Result { let Some(mut value) = self.current.to_digit(10) else { return Err(self.parse_error("Invalid number in duration".to_string())); }; while let Some(digit) = self.inc().and_then(|ch| ch.to_digit(10)) { value *= 10; value += digit; } Ok(value) } fn iso_to_ymd( &mut self, iso_year: u32, iso_week: u32, iso_day: u32, ) -> Result<(u32, u32, u32), ParseError> { if iso_week > 53 || iso_week > 52 && !is_long_year(iso_year as i32) { return Err(ParseError { index: self.idx, message: format!( "Invalid ISO date: week {iso_week} out of range for year {iso_year}" ), }); } if iso_day > 7 { return Err(ParseError { index: self.idx, message: "Invalid ISO date: week day is invalid".to_string(), }); } let ordinal: i32 = iso_week as i32 * 7 + iso_day as i32 - (week_day(iso_year as i32, 1, 4) as i32 + 3); self.ordinal_to_ymd(iso_year, ordinal, true) } fn ordinal_to_ymd( &mut self, year: u32, ordinal: i32, allow_out_of_bounds: bool, ) -> Result<(u32, u32, u32), ParseError> { let mut ord: i32 = ordinal; let mut y: u32 = year; let mut leap: usize = usize::from(is_leap(y as i32)); if ord < 1 { if !allow_out_of_bounds { return Err(self.parse_error(format!( "Invalid ordinal day: {ordinal} is too small for year {year}" ))); } // Previous year ord += days_in_year((year - 1) as i32) as i32; y -= 1; leap = usize::from(is_leap(y as i32)); } if ord > days_in_year(y as i32) as i32 { if !allow_out_of_bounds { return Err(self.parse_error(format!( "Invalid ordinal day: {ordinal} is too large for year {year}" ))); } // Next year ord -= days_in_year(y as i32) as i32; y += 1; leap = usize::from(is_leap(y as i32)); } for i in 1..14 { if ord < MONTHS_OFFSETS[leap][i] { let day = ord as u32 - MONTHS_OFFSETS[leap][i - 1] as u32; let month = (i - 1) as u32; return Ok((y, month, day)); } } Err(self.parse_error(format!( "Invalid ordinal day: {ordinal} is too large for year {year}" ))) } } pendulum-3.0.0/rust/src/python/000077500000000000000000000000001453741033500164555ustar00rootroot00000000000000pendulum-3.0.0/rust/src/python/helpers.rs000066400000000000000000000257011453741033500204720ustar00rootroot00000000000000use std::cmp::Ordering; use pyo3::{ intern, prelude::*, types::{PyDate, PyDateAccess, PyDateTime, PyDelta, PyDeltaAccess, PyString, PyTimeAccess}, PyTypeInfo, }; use crate::{ constants::{DAYS_PER_MONTHS, SECS_PER_DAY, SECS_PER_HOUR, SECS_PER_MIN}, helpers, }; use crate::python::types::PreciseDiff; struct DateTimeInfo<'py> { pub year: i32, pub month: i32, pub day: i32, pub hour: i32, pub minute: i32, pub second: i32, pub microsecond: i32, pub total_seconds: i32, pub offset: i32, pub tz: &'py str, pub is_datetime: bool, } impl PartialEq for DateTimeInfo<'_> { fn eq(&self, other: &Self) -> bool { ( self.year, self.month, self.day, self.hour, self.minute, self.second, self.microsecond, ) .eq(&( other.year, other.month, other.day, other.hour, other.minute, other.second, other.microsecond, )) } } impl PartialOrd for DateTimeInfo<'_> { fn partial_cmp(&self, other: &Self) -> Option { ( self.year, self.month, self.day, self.hour, self.minute, self.second, self.microsecond, ) .partial_cmp(&( other.year, other.month, other.day, other.hour, other.minute, other.second, other.microsecond, )) } } pub fn get_tz_name<'py>(py: Python, dt: &'py PyAny) -> PyResult<&'py str> { let tz: &str = ""; if !PyDateTime::is_type_of(dt) { return Ok(tz); } let tzinfo = dt.getattr("tzinfo"); match tzinfo { Err(_) => Ok(tz), Ok(tzinfo) => { if tzinfo.is_none() { return Ok(tz); } if tzinfo.hasattr(intern!(py, "key")).unwrap_or(false) { // zoneinfo timezone let tzname: &PyString = tzinfo .getattr(intern!(py, "key")) .unwrap() .downcast() .unwrap(); return tzname.to_str(); } else if tzinfo.hasattr(intern!(py, "name")).unwrap_or(false) { // Pendulum timezone let tzname: &PyString = tzinfo .getattr(intern!(py, "name")) .unwrap() .downcast() .unwrap(); return tzname.to_str(); } else if tzinfo.hasattr(intern!(py, "zone")).unwrap_or(false) { // pytz timezone let tzname: &PyString = tzinfo .getattr(intern!(py, "zone")) .unwrap() .downcast() .unwrap(); return tzname.to_str(); } Ok(tz) } } } pub fn get_offset(dt: &PyAny) -> PyResult { if !PyDateTime::is_type_of(dt) { return Ok(0); } let tzinfo = dt.getattr("tzinfo")?; if tzinfo.is_none() { return Ok(0); } let offset: &PyDelta = tzinfo.call_method1("utcoffset", (dt,))?.downcast()?; Ok(offset.get_days() * SECS_PER_DAY as i32 + offset.get_seconds()) } #[pyfunction] pub fn is_leap(year: i32) -> PyResult { Ok(helpers::is_leap(year)) } #[pyfunction] pub fn is_long_year(year: i32) -> PyResult { Ok(helpers::is_long_year(year)) } #[pyfunction] pub fn week_day(year: i32, month: u32, day: u32) -> PyResult { Ok(helpers::week_day(year, month, day)) } #[pyfunction] pub fn days_in_year(year: i32) -> PyResult { Ok(helpers::days_in_year(year)) } #[pyfunction] pub fn local_time( unix_time: f64, utc_offset: isize, microsecond: usize, ) -> PyResult<(usize, usize, usize, usize, usize, usize, usize)> { Ok(helpers::local_time(unix_time, utc_offset, microsecond)) } #[pyfunction] pub fn precise_diff<'py>(py: Python, dt1: &'py PyAny, dt2: &'py PyAny) -> PyResult { let mut sign = 1; let mut dtinfo1 = DateTimeInfo { year: dt1.downcast::()?.get_year(), month: i32::from(dt1.downcast::()?.get_month()), day: i32::from(dt1.downcast::()?.get_day()), hour: 0, minute: 0, second: 0, microsecond: 0, total_seconds: 0, tz: get_tz_name(py, dt1)?, offset: get_offset(dt1)?, is_datetime: PyDateTime::is_type_of(dt1), }; let mut dtinfo2 = DateTimeInfo { year: dt2.downcast::()?.get_year(), month: i32::from(dt2.downcast::()?.get_month()), day: i32::from(dt2.downcast::()?.get_day()), hour: 0, minute: 0, second: 0, microsecond: 0, total_seconds: 0, tz: get_tz_name(py, dt2)?, offset: get_offset(dt2)?, is_datetime: PyDateTime::is_type_of(dt2), }; let in_same_tz: bool = dtinfo1.tz == dtinfo2.tz && !dtinfo1.tz.is_empty(); let mut total_days = helpers::day_number(dtinfo2.year, dtinfo2.month as u8, dtinfo2.day as u8) - helpers::day_number(dtinfo1.year, dtinfo1.month as u8, dtinfo1.day as u8); if dtinfo1.is_datetime { let dt1dt: &PyDateTime = dt1.downcast()?; dtinfo1.hour = i32::from(dt1dt.get_hour()); dtinfo1.minute = i32::from(dt1dt.get_minute()); dtinfo1.second = i32::from(dt1dt.get_second()); dtinfo1.microsecond = dt1dt.get_microsecond() as i32; if !in_same_tz && dtinfo1.offset != 0 || total_days == 0 { dtinfo1.hour -= dtinfo1.offset / SECS_PER_HOUR as i32; dtinfo1.offset %= SECS_PER_HOUR as i32; dtinfo1.minute -= dtinfo1.offset / SECS_PER_MIN as i32; dtinfo1.offset %= SECS_PER_MIN as i32; dtinfo1.second -= dtinfo1.offset; if dtinfo1.second < 0 { dtinfo1.second += 60; dtinfo1.minute -= 1; } else if dtinfo1.second > 60 { dtinfo1.second -= 60; dtinfo1.minute += 1; } if dtinfo1.minute < 0 { dtinfo1.minute += 60; dtinfo1.hour -= 1; } else if dtinfo1.minute > 60 { dtinfo1.minute -= 60; dtinfo1.hour += 1; } if dtinfo1.hour < 0 { dtinfo1.hour += 24; dtinfo1.day -= 1; } else if dtinfo1.hour > 24 { dtinfo1.hour -= 24; dtinfo1.day += 1; } } dtinfo1.total_seconds = dtinfo1.hour * SECS_PER_HOUR as i32 + dtinfo1.minute * SECS_PER_MIN as i32 + dtinfo1.second; } if dtinfo2.is_datetime { let dt2dt: &PyDateTime = dt2.downcast()?; dtinfo2.hour = i32::from(dt2dt.get_hour()); dtinfo2.minute = i32::from(dt2dt.get_minute()); dtinfo2.second = i32::from(dt2dt.get_second()); dtinfo2.microsecond = dt2dt.get_microsecond() as i32; if !in_same_tz && dtinfo2.offset != 0 || total_days == 0 { dtinfo2.hour -= dtinfo2.offset / SECS_PER_HOUR as i32; dtinfo2.offset %= SECS_PER_HOUR as i32; dtinfo2.minute -= dtinfo2.offset / SECS_PER_MIN as i32; dtinfo2.offset %= SECS_PER_MIN as i32; dtinfo2.second -= dtinfo2.offset; if dtinfo2.second < 0 { dtinfo2.second += 60; dtinfo2.minute -= 1; } else if dtinfo2.second > 60 { dtinfo2.second -= 60; dtinfo2.minute += 1; } if dtinfo2.minute < 0 { dtinfo2.minute += 60; dtinfo2.hour -= 1; } else if dtinfo2.minute > 60 { dtinfo2.minute -= 60; dtinfo2.hour += 1; } if dtinfo2.hour < 0 { dtinfo2.hour += 24; dtinfo2.day -= 1; } else if dtinfo2.hour > 24 { dtinfo2.hour -= 24; dtinfo2.day += 1; } } dtinfo2.total_seconds = dtinfo2.hour * SECS_PER_HOUR as i32 + dtinfo2.minute * SECS_PER_MIN as i32 + dtinfo2.second; } if dtinfo1 > dtinfo2 { sign = -1; (dtinfo1, dtinfo2) = (dtinfo2, dtinfo1); total_days = -total_days; } let mut year_diff = dtinfo2.year - dtinfo1.year; let mut month_diff = dtinfo2.month - dtinfo1.month; let mut day_diff = dtinfo2.day - dtinfo1.day; let mut hour_diff = dtinfo2.hour - dtinfo1.hour; let mut minute_diff = dtinfo2.minute - dtinfo1.minute; let mut second_diff = dtinfo2.second - dtinfo1.second; let mut microsecond_diff = dtinfo2.microsecond - dtinfo1.microsecond; if microsecond_diff < 0 { microsecond_diff += 1_000_000; second_diff -= 1; } if second_diff < 0 { second_diff += 60; minute_diff -= 1; } if minute_diff < 0 { minute_diff += 60; hour_diff -= 1; } if hour_diff < 0 { hour_diff += 24; day_diff -= 1; } if day_diff < 0 { // If we have a difference in days, // we have to check if they represent months let mut year = dtinfo2.year; let mut month = dtinfo2.month; if month == 1 { month = 12; year -= 1; } else { month -= 1; } let leap = helpers::is_leap(year); let days_in_last_month = DAYS_PER_MONTHS[usize::from(leap)][month as usize]; let days_in_month = DAYS_PER_MONTHS[usize::from(helpers::is_leap(dtinfo2.year))][dtinfo2.month as usize]; match day_diff.cmp(&(days_in_month - days_in_last_month)) { Ordering::Less => { // We don't have a full month, we calculate days if days_in_last_month < dtinfo1.day { day_diff += dtinfo1.day; } else { day_diff += days_in_last_month; } } Ordering::Equal => { // We have exactly a full month // We remove the days difference // and add one to the months difference day_diff = 0; month_diff += 1; } Ordering::Greater => { // We have a full month day_diff += days_in_last_month; } } month_diff -= 1; } if month_diff < 0 { month_diff += 12; year_diff -= 1; } Ok(PreciseDiff { years: year_diff * sign, months: month_diff * sign, days: day_diff * sign, hours: hour_diff * sign, minutes: minute_diff * sign, seconds: second_diff * sign, microseconds: microsecond_diff * sign, total_days: total_days * sign, }) } pendulum-3.0.0/rust/src/python/mod.rs000066400000000000000000000016031453741033500176020ustar00rootroot00000000000000use pyo3::prelude::*; mod helpers; mod parsing; mod types; use helpers::{days_in_year, is_leap, is_long_year, local_time, precise_diff, week_day}; use parsing::parse_iso8601; use types::{Duration, PreciseDiff}; #[pymodule] pub fn _pendulum(_py: Python<'_>, m: &PyModule) -> PyResult<()> { m.add_function(wrap_pyfunction!(days_in_year, m)?)?; m.add_function(wrap_pyfunction!(is_leap, m)?)?; m.add_function(wrap_pyfunction!(is_long_year, m)?)?; m.add_function(wrap_pyfunction!(local_time, m)?)?; m.add_function(wrap_pyfunction!(week_day, m)?)?; m.add_function(wrap_pyfunction!(parse_iso8601, m)?)?; m.add_function(wrap_pyfunction!(precise_diff, m)?)?; m.add_class::()?; m.add_class::()?; #[cfg(not(feature = "mimalloc"))] m.setattr("__pendulum_default_allocator__", true)?; // uses setattr so this is not in __all__ Ok(()) } pendulum-3.0.0/rust/src/python/parsing.rs000066400000000000000000000105161453741033500204710ustar00rootroot00000000000000use pyo3::exceptions; use pyo3::prelude::*; use pyo3::types::PyDate; use pyo3::types::PyDateTime; use pyo3::types::PyTime; use crate::parsing::Parser; use crate::python::types::{Duration, FixedTimezone}; #[pyfunction] pub fn parse_iso8601(py: Python, input: &str) -> PyResult { let parsed = Parser::new(input).parse(); match parsed { Ok(parsed) => match (parsed.datetime, parsed.duration, parsed.second_datetime) { (Some(datetime), None, None) => match (datetime.has_date, datetime.has_time) { (true, true) => match datetime.offset { Some(offset) => { let dt = PyDateTime::new( py, datetime.year as i32, datetime.month as u8, datetime.day as u8, datetime.hour as u8, datetime.minute as u8, datetime.second as u8, datetime.microsecond, Some( Py::new(py, FixedTimezone::new(offset, datetime.tzname))? .to_object(py) .extract(py)?, ), )?; Ok(dt.to_object(py)) } None => { let dt = PyDateTime::new( py, datetime.year as i32, datetime.month as u8, datetime.day as u8, datetime.hour as u8, datetime.minute as u8, datetime.second as u8, datetime.microsecond, None, )?; Ok(dt.to_object(py)) } }, (true, false) => { let dt = PyDate::new( py, datetime.year as i32, datetime.month as u8, datetime.day as u8, )?; Ok(dt.to_object(py)) } (false, true) => match datetime.offset { Some(offset) => { let dt = PyTime::new( py, datetime.hour as u8, datetime.minute as u8, datetime.second as u8, datetime.microsecond, Some( Py::new(py, FixedTimezone::new(offset, datetime.tzname))? .to_object(py) .extract(py)?, ), )?; Ok(dt.to_object(py)) } None => { let dt = PyTime::new( py, datetime.hour as u8, datetime.minute as u8, datetime.second as u8, datetime.microsecond, None, )?; Ok(dt.to_object(py)) } }, (_, _) => Err(exceptions::PyValueError::new_err( "Parsing error".to_string(), )), }, (None, Some(duration), None) => Ok(Py::new( py, Duration::new( Some(duration.years), Some(duration.months), Some(duration.weeks), Some(duration.days), Some(duration.hours), Some(duration.minutes), Some(duration.seconds), Some(duration.microseconds), ), )? .to_object(py)), (_, _, _) => Err(exceptions::PyValueError::new_err( "Not yet implemented".to_string(), )), }, Err(error) => Err(exceptions::PyValueError::new_err(error.to_string())), } } pendulum-3.0.0/rust/src/python/types/000077500000000000000000000000001453741033500176215ustar00rootroot00000000000000pendulum-3.0.0/rust/src/python/types/duration.rs000066400000000000000000000026401453741033500220160ustar00rootroot00000000000000use pyo3::prelude::*; #[pyclass(module = "_pendulum")] pub struct Duration { #[pyo3(get, set)] pub years: u32, #[pyo3(get, set)] pub months: u32, #[pyo3(get, set)] pub weeks: u32, #[pyo3(get, set)] pub days: u32, #[pyo3(get, set)] pub hours: u32, #[pyo3(get, set)] pub minutes: u32, #[pyo3(get, set)] pub seconds: u32, #[pyo3(get, set)] pub microseconds: u32, } #[pymethods] impl Duration { #[new] #[pyo3(signature = (years=0, months=0, weeks=0, days=0, hours=0, minutes=0, seconds=0, microseconds=0))] #[allow(clippy::too_many_arguments)] pub fn new( years: Option, months: Option, weeks: Option, days: Option, hours: Option, minutes: Option, seconds: Option, microseconds: Option, ) -> Self { Self { years: years.unwrap_or(0), months: months.unwrap_or(0), weeks: weeks.unwrap_or(0), days: days.unwrap_or(0), hours: hours.unwrap_or(0), minutes: minutes.unwrap_or(0), seconds: seconds.unwrap_or(0), microseconds: microseconds.unwrap_or(0), } } #[getter] fn remaining_days(&self) -> PyResult { Ok(self.days) } #[getter] fn remaining_seconds(&self) -> PyResult { Ok(self.seconds) } } pendulum-3.0.0/rust/src/python/types/interval.rs000066400000000000000000000020211453741033500220060ustar00rootroot00000000000000use pyo3::prelude::*; use pyo3::types::PyDelta; #[pyclass(extends=PyDelta)] #[derive(Default)] pub struct Interval { pub days: i32, pub seconds: i32, pub microseconds: i32, } #[pymethods] impl Interval { #[new] #[pyo3(signature = (days=0, seconds=0, microseconds=0, milliseconds=0, minutes=0, hours=0, weeks=0))] pub fn new( py: Python, days: Option, seconds: Option, microseconds: Option, milliseconds: Option, minutes: Option, hours: Option, weeks: Option, ) -> PyResult { println!("{} days", 31); PyDelta::new( py, days.unwrap_or(0), seconds.unwrap_or(0), microseconds.unwrap_or(0), true, )?; let f = Ok(Self { days: days.unwrap_or(0), seconds: seconds.unwrap_or(0), microseconds: microseconds.unwrap_or(0), }); println!("{} days", 31); f } } pendulum-3.0.0/rust/src/python/types/mod.rs000066400000000000000000000002171453741033500207460ustar00rootroot00000000000000mod duration; mod precise_diff; mod timezone; pub use duration::Duration; pub use precise_diff::PreciseDiff; pub use timezone::FixedTimezone; pendulum-3.0.0/rust/src/python/types/precise_diff.rs000066400000000000000000000030341453741033500226110ustar00rootroot00000000000000use pyo3::prelude::*; #[pyclass(module = "_pendulum")] pub struct PreciseDiff { #[pyo3(get, set)] pub years: i32, #[pyo3(get, set)] pub months: i32, #[pyo3(get, set)] pub days: i32, #[pyo3(get, set)] pub hours: i32, #[pyo3(get, set)] pub minutes: i32, #[pyo3(get, set)] pub seconds: i32, #[pyo3(get, set)] pub microseconds: i32, #[pyo3(get, set)] pub total_days: i32, } #[pymethods] impl PreciseDiff { #[new] #[pyo3(signature = (years=0, months=0, days=0, hours=0, minutes=0, seconds=0, microseconds=0, total_days=0))] #[allow(clippy::too_many_arguments)] pub fn new( years: Option, months: Option, days: Option, hours: Option, minutes: Option, seconds: Option, microseconds: Option, total_days: Option, ) -> Self { Self { years: years.unwrap_or(0), months: months.unwrap_or(0), days: days.unwrap_or(0), hours: hours.unwrap_or(0), minutes: minutes.unwrap_or(0), seconds: seconds.unwrap_or(0), microseconds: microseconds.unwrap_or(0), total_days: total_days.unwrap_or(0), } } fn __repr__(&self) -> String { format!("PreciseDiff(years={}, months={}, days={}, hours={}, minutes={}, seconds={}, microseconds={}, total_days={})", self.years, self.months, self.days, self.hours, self.minutes, self.seconds, self.microseconds, self.total_days) } } pendulum-3.0.0/rust/src/python/types/timezone.rs000066400000000000000000000024741453741033500220300ustar00rootroot00000000000000use pyo3::prelude::*; use pyo3::types::{PyDelta, PyDict, PyTzInfo}; #[pyclass(module = "_pendulum", extends = PyTzInfo)] #[derive(Clone)] pub struct FixedTimezone { offset: i32, name: Option, } #[pymethods] impl FixedTimezone { #[new] pub fn new(offset: i32, name: Option) -> Self { Self { offset, name } } fn utcoffset<'p>(&self, py: Python<'p>, _dt: &PyAny) -> PyResult<&'p PyDelta> { PyDelta::new(py, 0, self.offset, 0, true) } fn tzname(&self, _dt: &PyAny) -> String { self.__str__() } fn dst<'p>(&self, py: Python<'p>, _dt: &PyAny) -> PyResult<&'p PyDelta> { PyDelta::new(py, 0, 0, 0, true) } fn __repr__(&self) -> String { format!( "FixedTimezone({}, name=\"{}\")", self.offset, self.__str__() ) } fn __str__(&self) -> String { if let Some(n) = &self.name { n.clone() } else { let sign = if self.offset < 0 { "-" } else { "+" }; let minutes = self.offset.abs() / 60; let (hour, minute) = (minutes / 60, minutes % 60); format!("{sign}{hour:.2}:{minute:.2}") } } fn __deepcopy__(&self, py: Python, _memo: &PyDict) -> PyResult> { Py::new(py, self.clone()) } } pendulum-3.0.0/src/000077500000000000000000000000001453741033500141375ustar00rootroot00000000000000pendulum-3.0.0/src/pendulum/000077500000000000000000000000001453741033500157705ustar00rootroot00000000000000pendulum-3.0.0/src/pendulum/__init__.py000066400000000000000000000220411453741033500201000ustar00rootroot00000000000000from __future__ import annotations import datetime as _datetime from typing import Union from typing import cast from typing import overload from pendulum.__version__ import __version__ from pendulum.constants import DAYS_PER_WEEK from pendulum.constants import HOURS_PER_DAY from pendulum.constants import MINUTES_PER_HOUR from pendulum.constants import MONTHS_PER_YEAR from pendulum.constants import SECONDS_PER_DAY from pendulum.constants import SECONDS_PER_HOUR from pendulum.constants import SECONDS_PER_MINUTE from pendulum.constants import WEEKS_PER_YEAR from pendulum.constants import YEARS_PER_CENTURY from pendulum.constants import YEARS_PER_DECADE from pendulum.date import Date from pendulum.datetime import DateTime from pendulum.day import WeekDay from pendulum.duration import Duration from pendulum.formatting import Formatter from pendulum.helpers import format_diff from pendulum.helpers import get_locale from pendulum.helpers import locale from pendulum.helpers import set_locale from pendulum.helpers import week_ends_at from pendulum.helpers import week_starts_at from pendulum.interval import Interval from pendulum.parser import parse from pendulum.testing.traveller import Traveller from pendulum.time import Time from pendulum.tz import UTC from pendulum.tz import fixed_timezone from pendulum.tz import local_timezone from pendulum.tz import set_local_timezone from pendulum.tz import test_local_timezone from pendulum.tz import timezones from pendulum.tz.timezone import FixedTimezone from pendulum.tz.timezone import Timezone MONDAY = WeekDay.MONDAY TUESDAY = WeekDay.TUESDAY WEDNESDAY = WeekDay.WEDNESDAY THURSDAY = WeekDay.THURSDAY FRIDAY = WeekDay.FRIDAY SATURDAY = WeekDay.SATURDAY SUNDAY = WeekDay.SUNDAY _TEST_NOW: DateTime | None = None _LOCALE = "en" _WEEK_STARTS_AT: WeekDay = WeekDay.MONDAY _WEEK_ENDS_AT: WeekDay = WeekDay.SUNDAY _formatter = Formatter() @overload def timezone(name: int) -> FixedTimezone: ... @overload def timezone(name: str) -> Timezone: ... @overload def timezone(name: str | int) -> Timezone | FixedTimezone: ... def timezone(name: str | int) -> Timezone | FixedTimezone: """ Return a Timezone instance given its name. """ if isinstance(name, int): return fixed_timezone(name) if name.lower() == "utc": return UTC return Timezone(name) def _safe_timezone( obj: str | float | _datetime.tzinfo | Timezone | FixedTimezone | None, dt: _datetime.datetime | None = None, ) -> Timezone | FixedTimezone: """ Creates a timezone instance from a string, Timezone, TimezoneInfo or integer offset. """ if isinstance(obj, (Timezone, FixedTimezone)): return obj if obj is None or obj == "local": return local_timezone() if isinstance(obj, (int, float)): obj = int(obj * 60 * 60) elif isinstance(obj, _datetime.tzinfo): # zoneinfo if hasattr(obj, "key"): obj = obj.key # pytz elif hasattr(obj, "localize"): obj = obj.zone # type: ignore[attr-defined] elif obj.tzname(None) == "UTC": return UTC else: offset = obj.utcoffset(dt) if offset is None: offset = _datetime.timedelta(0) obj = int(offset.total_seconds()) obj = cast(Union[str, int], obj) return timezone(obj) # Public API def datetime( year: int, month: int, day: int, hour: int = 0, minute: int = 0, second: int = 0, microsecond: int = 0, tz: str | float | Timezone | FixedTimezone | _datetime.tzinfo | None = UTC, fold: int = 1, raise_on_unknown_times: bool = False, ) -> DateTime: """ Creates a new DateTime instance from a specific date and time. """ return DateTime.create( year, month, day, hour=hour, minute=minute, second=second, microsecond=microsecond, tz=tz, fold=fold, raise_on_unknown_times=raise_on_unknown_times, ) def local( year: int, month: int, day: int, hour: int = 0, minute: int = 0, second: int = 0, microsecond: int = 0, ) -> DateTime: """ Return a DateTime in the local timezone. """ return datetime( year, month, day, hour, minute, second, microsecond, tz=local_timezone() ) def naive( year: int, month: int, day: int, hour: int = 0, minute: int = 0, second: int = 0, microsecond: int = 0, fold: int = 1, ) -> DateTime: """ Return a naive DateTime. """ return DateTime(year, month, day, hour, minute, second, microsecond, fold=fold) def date(year: int, month: int, day: int) -> Date: """ Create a new Date instance. """ return Date(year, month, day) def time(hour: int, minute: int = 0, second: int = 0, microsecond: int = 0) -> Time: """ Create a new Time instance. """ return Time(hour, minute, second, microsecond) @overload def instance( obj: _datetime.datetime, tz: str | Timezone | FixedTimezone | _datetime.tzinfo | None = UTC, ) -> DateTime: ... @overload def instance( obj: _datetime.date, tz: str | Timezone | FixedTimezone | _datetime.tzinfo | None = UTC, ) -> Date: ... @overload def instance( obj: _datetime.time, tz: str | Timezone | FixedTimezone | _datetime.tzinfo | None = UTC, ) -> Time: ... def instance( obj: _datetime.datetime | _datetime.date | _datetime.time, tz: str | Timezone | FixedTimezone | _datetime.tzinfo | None = UTC, ) -> DateTime | Date | Time: """ Create a DateTime/Date/Time instance from a datetime/date/time native one. """ if isinstance(obj, (DateTime, Date, Time)): return obj if isinstance(obj, _datetime.date) and not isinstance(obj, _datetime.datetime): return date(obj.year, obj.month, obj.day) if isinstance(obj, _datetime.time): return Time.instance(obj, tz=tz) return DateTime.instance(obj, tz=tz) def now(tz: str | Timezone | None = None) -> DateTime: """ Get a DateTime instance for the current date and time. """ return DateTime.now(tz) def today(tz: str | Timezone = "local") -> DateTime: """ Create a DateTime instance for today. """ return now(tz).start_of("day") def tomorrow(tz: str | Timezone = "local") -> DateTime: """ Create a DateTime instance for tomorrow. """ return today(tz).add(days=1) def yesterday(tz: str | Timezone = "local") -> DateTime: """ Create a DateTime instance for yesterday. """ return today(tz).subtract(days=1) def from_format( string: str, fmt: str, tz: str | Timezone = UTC, locale: str | None = None, ) -> DateTime: """ Creates a DateTime instance from a specific format. """ parts = _formatter.parse(string, fmt, now(tz=tz), locale=locale) if parts["tz"] is None: parts["tz"] = tz return datetime(**parts) def from_timestamp(timestamp: int | float, tz: str | Timezone = UTC) -> DateTime: """ Create a DateTime instance from a timestamp. """ dt = _datetime.datetime.utcfromtimestamp(timestamp) dt = datetime( dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, dt.microsecond ) if tz is not UTC or tz != "UTC": dt = dt.in_timezone(tz) return dt def duration( days: float = 0, seconds: float = 0, microseconds: float = 0, milliseconds: float = 0, minutes: float = 0, hours: float = 0, weeks: float = 0, years: float = 0, months: float = 0, ) -> Duration: """ Create a Duration instance. """ return Duration( days=days, seconds=seconds, microseconds=microseconds, milliseconds=milliseconds, minutes=minutes, hours=hours, weeks=weeks, years=years, months=months, ) def interval(start: DateTime, end: DateTime, absolute: bool = False) -> Interval: """ Create an Interval instance. """ return Interval(start, end, absolute=absolute) # Testing _traveller = Traveller(DateTime) freeze = _traveller.freeze travel = _traveller.travel travel_to = _traveller.travel_to travel_back = _traveller.travel_back __all__ = [ "__version__", "DAYS_PER_WEEK", "HOURS_PER_DAY", "MINUTES_PER_HOUR", "MONTHS_PER_YEAR", "SECONDS_PER_DAY", "SECONDS_PER_HOUR", "SECONDS_PER_MINUTE", "WEEKS_PER_YEAR", "YEARS_PER_CENTURY", "YEARS_PER_DECADE", "Date", "DateTime", "Duration", "Formatter", "WeekDay", "date", "datetime", "duration", "format_diff", "freeze", "from_format", "from_timestamp", "get_locale", "instance", "interval", "local", "locale", "naive", "now", "set_locale", "week_ends_at", "week_starts_at", "parse", "Interval", "Time", "UTC", "local_timezone", "set_local_timezone", "test_local_timezone", "time", "timezone", "timezones", "today", "tomorrow", "travel", "travel_back", "travel_to", "FixedTimezone", "Timezone", "yesterday", ] pendulum-3.0.0/src/pendulum/__version__.py000066400000000000000000000000731453741033500206230ustar00rootroot00000000000000from __future__ import annotations __version__ = "3.0.0" pendulum-3.0.0/src/pendulum/_helpers.py000066400000000000000000000211141453741033500201420ustar00rootroot00000000000000from __future__ import annotations import datetime import math from typing import NamedTuple from typing import cast from pendulum.constants import DAY_OF_WEEK_TABLE from pendulum.constants import DAYS_PER_L_YEAR from pendulum.constants import DAYS_PER_MONTHS from pendulum.constants import DAYS_PER_N_YEAR from pendulum.constants import EPOCH_YEAR from pendulum.constants import MONTHS_OFFSETS from pendulum.constants import SECS_PER_4_YEARS from pendulum.constants import SECS_PER_100_YEARS from pendulum.constants import SECS_PER_400_YEARS from pendulum.constants import SECS_PER_DAY from pendulum.constants import SECS_PER_HOUR from pendulum.constants import SECS_PER_MIN from pendulum.constants import SECS_PER_YEAR from pendulum.constants import TM_DECEMBER from pendulum.constants import TM_JANUARY from pendulum.tz.timezone import Timezone from pendulum.utils._compat import zoneinfo class PreciseDiff(NamedTuple): years: int months: int days: int hours: int minutes: int seconds: int microseconds: int total_days: int def __repr__(self) -> str: return ( f"{self.years} years " f"{self.months} months " f"{self.days} days " f"{self.hours} hours " f"{self.minutes} minutes " f"{self.seconds} seconds " f"{self.microseconds} microseconds" ) def is_leap(year: int) -> bool: return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0) def is_long_year(year: int) -> bool: def p(y: int) -> int: return y + y // 4 - y // 100 + y // 400 return p(year) % 7 == 4 or p(year - 1) % 7 == 3 def week_day(year: int, month: int, day: int) -> int: if month < 3: year -= 1 w = ( year + year // 4 - year // 100 + year // 400 + DAY_OF_WEEK_TABLE[month - 1] + day ) % 7 if not w: w = 7 return w def days_in_year(year: int) -> int: if is_leap(year): return DAYS_PER_L_YEAR return DAYS_PER_N_YEAR def local_time( unix_time: int, utc_offset: int, microseconds: int ) -> tuple[int, int, int, int, int, int, int]: """ Returns a UNIX time as a broken-down time for a particular transition type. """ year = EPOCH_YEAR seconds = math.floor(unix_time) # Shift to a base year that is 400-year aligned. if seconds >= 0: seconds -= 10957 * SECS_PER_DAY year += 30 # == 2000 else: seconds += (146097 - 10957) * SECS_PER_DAY year -= 370 # == 1600 seconds += utc_offset # Handle years in chunks of 400/100/4/1 year += 400 * (seconds // SECS_PER_400_YEARS) seconds %= SECS_PER_400_YEARS if seconds < 0: seconds += SECS_PER_400_YEARS year -= 400 leap_year = 1 # 4-century aligned sec_per_100years = SECS_PER_100_YEARS[leap_year] while seconds >= sec_per_100years: seconds -= sec_per_100years year += 100 leap_year = 0 # 1-century, non 4-century aligned sec_per_100years = SECS_PER_100_YEARS[leap_year] sec_per_4years = SECS_PER_4_YEARS[leap_year] while seconds >= sec_per_4years: seconds -= sec_per_4years year += 4 leap_year = 1 # 4-year, non century aligned sec_per_4years = SECS_PER_4_YEARS[leap_year] sec_per_year = SECS_PER_YEAR[leap_year] while seconds >= sec_per_year: seconds -= sec_per_year year += 1 leap_year = 0 # non 4-year aligned sec_per_year = SECS_PER_YEAR[leap_year] # Handle months and days month = TM_DECEMBER + 1 day = seconds // SECS_PER_DAY + 1 seconds %= SECS_PER_DAY while month != TM_JANUARY + 1: month_offset = MONTHS_OFFSETS[leap_year][month] if day > month_offset: day -= month_offset break month -= 1 # Handle hours, minutes, seconds and microseconds hour, seconds = divmod(seconds, SECS_PER_HOUR) minute, second = divmod(seconds, SECS_PER_MIN) return year, month, day, hour, minute, second, microseconds def precise_diff( d1: datetime.datetime | datetime.date, d2: datetime.datetime | datetime.date ) -> PreciseDiff: """ Calculate a precise difference between two datetimes. :param d1: The first datetime :param d2: The second datetime """ sign = 1 if d1 == d2: return PreciseDiff(0, 0, 0, 0, 0, 0, 0, 0) tzinfo1: datetime.tzinfo | None = ( d1.tzinfo if isinstance(d1, datetime.datetime) else None ) tzinfo2: datetime.tzinfo | None = ( d2.tzinfo if isinstance(d2, datetime.datetime) else None ) if ( tzinfo1 is None and tzinfo2 is not None or tzinfo2 is None and tzinfo1 is not None ): raise ValueError( "Comparison between naive and aware datetimes is not supported" ) if d1 > d2: d1, d2 = d2, d1 sign = -1 d_diff = 0 hour_diff = 0 min_diff = 0 sec_diff = 0 mic_diff = 0 total_days = _day_number(d2.year, d2.month, d2.day) - _day_number( d1.year, d1.month, d1.day ) in_same_tz = False tz1 = None tz2 = None # Trying to figure out the timezone names # If we can't find them, we assume different timezones if tzinfo1 and tzinfo2: tz1 = _get_tzinfo_name(tzinfo1) tz2 = _get_tzinfo_name(tzinfo2) in_same_tz = tz1 == tz2 and tz1 is not None if isinstance(d2, datetime.datetime): if isinstance(d1, datetime.datetime): # If we are not in the same timezone # we need to adjust # # We also need to adjust if we do not # have variable-length units if not in_same_tz or total_days == 0: offset1 = d1.utcoffset() offset2 = d2.utcoffset() if offset1: d1 = d1 - offset1 if offset2: d2 = d2 - offset2 hour_diff = d2.hour - d1.hour min_diff = d2.minute - d1.minute sec_diff = d2.second - d1.second mic_diff = d2.microsecond - d1.microsecond else: hour_diff = d2.hour min_diff = d2.minute sec_diff = d2.second mic_diff = d2.microsecond if mic_diff < 0: mic_diff += 1000000 sec_diff -= 1 if sec_diff < 0: sec_diff += 60 min_diff -= 1 if min_diff < 0: min_diff += 60 hour_diff -= 1 if hour_diff < 0: hour_diff += 24 d_diff -= 1 y_diff = d2.year - d1.year m_diff = d2.month - d1.month d_diff += d2.day - d1.day if d_diff < 0: year = d2.year month = d2.month if month == 1: month = 12 year -= 1 else: month -= 1 leap = int(is_leap(year)) days_in_last_month = DAYS_PER_MONTHS[leap][month] days_in_month = DAYS_PER_MONTHS[int(is_leap(d2.year))][d2.month] if d_diff < days_in_month - days_in_last_month: # We don't have a full month, we calculate days if days_in_last_month < d1.day: d_diff += d1.day else: d_diff += days_in_last_month elif d_diff == days_in_month - days_in_last_month: # We have exactly a full month # We remove the days difference # and add one to the months difference d_diff = 0 m_diff += 1 else: # We have a full month d_diff += days_in_last_month m_diff -= 1 if m_diff < 0: m_diff += 12 y_diff -= 1 return PreciseDiff( sign * y_diff, sign * m_diff, sign * d_diff, sign * hour_diff, sign * min_diff, sign * sec_diff, sign * mic_diff, sign * total_days, ) def _day_number(year: int, month: int, day: int) -> int: month = (month + 9) % 12 year = year - month // 10 return ( 365 * year + year // 4 - year // 100 + year // 400 + (month * 306 + 5) // 10 + (day - 1) ) def _get_tzinfo_name(tzinfo: datetime.tzinfo | None) -> str | None: if tzinfo is None: return None if hasattr(tzinfo, "key"): # zoneinfo timezone return cast(zoneinfo.ZoneInfo, tzinfo).key elif hasattr(tzinfo, "name"): # Pendulum timezone return cast(Timezone, tzinfo).name elif hasattr(tzinfo, "zone"): # pytz timezone return tzinfo.zone # type: ignore[no-any-return] return None pendulum-3.0.0/src/pendulum/_pendulum.pyi000066400000000000000000000017621453741033500205110ustar00rootroot00000000000000from __future__ import annotations from datetime import date from datetime import datetime from datetime import time from typing import NamedTuple class Duration: years: int = 0 months: int = 0 weeks: int = 0 days: int = 0 remaining_days: int = 0 hours: int = 0 minutes: int = 0 seconds: int = 0 remaining_seconds: int = 0 microseconds: int = 0 class PreciseDiff(NamedTuple): years: int months: int days: int hours: int minutes: int seconds: int microseconds: int total_days: int def parse_iso8601( text: str, ) -> datetime | date | time | Duration: ... def days_in_year(year: int) -> int: ... def is_leap(year: int) -> bool: ... def is_long_year(year: int) -> bool: ... def local_time( unix_time: int, utc_offset: int, microseconds: int ) -> tuple[int, int, int, int, int, int, int]: ... def precise_diff(d1: datetime | date, d2: datetime | date) -> PreciseDiff: ... def week_day(year: int, month: int, day: int) -> int: ... pendulum-3.0.0/src/pendulum/constants.py000066400000000000000000000053151453741033500203620ustar00rootroot00000000000000# The day constants from __future__ import annotations # Number of X in Y. YEARS_PER_CENTURY = 100 YEARS_PER_DECADE = 10 MONTHS_PER_YEAR = 12 WEEKS_PER_YEAR = 52 DAYS_PER_WEEK = 7 HOURS_PER_DAY = 24 MINUTES_PER_HOUR = 60 SECONDS_PER_MINUTE = 60 SECONDS_PER_HOUR = MINUTES_PER_HOUR * SECONDS_PER_MINUTE SECONDS_PER_DAY = HOURS_PER_DAY * SECONDS_PER_HOUR US_PER_SECOND = 1000000 # Formats ATOM = "YYYY-MM-DDTHH:mm:ssZ" COOKIE = "dddd, DD-MMM-YYYY HH:mm:ss zz" ISO8601 = "YYYY-MM-DDTHH:mm:ssZ" ISO8601_EXTENDED = "YYYY-MM-DDTHH:mm:ss.SSSSSSZ" RFC822 = "ddd, DD MMM YY HH:mm:ss ZZ" RFC850 = "dddd, DD-MMM-YY HH:mm:ss zz" RFC1036 = "ddd, DD MMM YY HH:mm:ss ZZ" RFC1123 = "ddd, DD MMM YYYY HH:mm:ss ZZ" RFC2822 = "ddd, DD MMM YYYY HH:mm:ss ZZ" RFC3339 = ISO8601 RFC3339_EXTENDED = ISO8601_EXTENDED RSS = "ddd, DD MMM YYYY HH:mm:ss ZZ" W3C = ISO8601 EPOCH_YEAR = 1970 DAYS_PER_N_YEAR = 365 DAYS_PER_L_YEAR = 366 USECS_PER_SEC = 1000000 SECS_PER_MIN = 60 SECS_PER_HOUR = 60 * SECS_PER_MIN SECS_PER_DAY = SECS_PER_HOUR * 24 # 400-year chunks always have 146097 days (20871 weeks). SECS_PER_400_YEARS = 146097 * SECS_PER_DAY # The number of seconds in an aligned 100-year chunk, for those that # do not begin with a leap year and those that do respectively. SECS_PER_100_YEARS = ( (76 * DAYS_PER_N_YEAR + 24 * DAYS_PER_L_YEAR) * SECS_PER_DAY, (75 * DAYS_PER_N_YEAR + 25 * DAYS_PER_L_YEAR) * SECS_PER_DAY, ) # The number of seconds in an aligned 4-year chunk, for those that # do not begin with a leap year and those that do respectively. SECS_PER_4_YEARS = ( (4 * DAYS_PER_N_YEAR + 0 * DAYS_PER_L_YEAR) * SECS_PER_DAY, (3 * DAYS_PER_N_YEAR + 1 * DAYS_PER_L_YEAR) * SECS_PER_DAY, ) # The number of seconds in non-leap and leap years respectively. SECS_PER_YEAR = (DAYS_PER_N_YEAR * SECS_PER_DAY, DAYS_PER_L_YEAR * SECS_PER_DAY) DAYS_PER_YEAR = (DAYS_PER_N_YEAR, DAYS_PER_L_YEAR) # The month lengths in non-leap and leap years respectively. DAYS_PER_MONTHS = ( (-1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31), (-1, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31), ) # The day offsets of the beginning of each (1-based) month in non-leap # and leap years respectively. # For example, in a leap year there are 335 days before December. MONTHS_OFFSETS = ( (-1, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365), (-1, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366), ) DAY_OF_WEEK_TABLE = (0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4) TM_SUNDAY = 0 TM_MONDAY = 1 TM_TUESDAY = 2 TM_WEDNESDAY = 3 TM_THURSDAY = 4 TM_FRIDAY = 5 TM_SATURDAY = 6 TM_JANUARY = 0 TM_FEBRUARY = 1 TM_MARCH = 2 TM_APRIL = 3 TM_MAY = 4 TM_JUNE = 5 TM_JULY = 6 TM_AUGUST = 7 TM_SEPTEMBER = 8 TM_OCTOBER = 9 TM_NOVEMBER = 10 TM_DECEMBER = 11 pendulum-3.0.0/src/pendulum/date.py000066400000000000000000000570071453741033500172700ustar00rootroot00000000000000# The following is only needed because of Python 3.7 # mypy: no-warn-unused-ignores from __future__ import annotations import calendar import math from datetime import date from datetime import datetime from datetime import timedelta from typing import TYPE_CHECKING from typing import ClassVar from typing import NoReturn from typing import cast from typing import overload import pendulum from pendulum.constants import MONTHS_PER_YEAR from pendulum.constants import YEARS_PER_CENTURY from pendulum.constants import YEARS_PER_DECADE from pendulum.day import WeekDay from pendulum.exceptions import PendulumException from pendulum.helpers import add_duration from pendulum.interval import Interval from pendulum.mixins.default import FormattableMixin if TYPE_CHECKING: from typing_extensions import Self from typing_extensions import SupportsIndex class Date(FormattableMixin, date): _MODIFIERS_VALID_UNITS: ClassVar[list[str]] = [ "day", "week", "month", "year", "decade", "century", ] # Getters/Setters def set( self, year: int | None = None, month: int | None = None, day: int | None = None ) -> Self: return self.replace(year=year, month=month, day=day) @property def day_of_week(self) -> WeekDay: """ Returns the day of the week (0-6). """ return WeekDay(self.weekday()) @property def day_of_year(self) -> int: """ Returns the day of the year (1-366). """ k = 1 if self.is_leap_year() else 2 return (275 * self.month) // 9 - k * ((self.month + 9) // 12) + self.day - 30 @property def week_of_year(self) -> int: return self.isocalendar()[1] @property def days_in_month(self) -> int: return calendar.monthrange(self.year, self.month)[1] @property def week_of_month(self) -> int: return math.ceil((self.day + self.first_of("month").isoweekday() - 1) / 7) @property def age(self) -> int: return self.diff(abs=False).in_years() @property def quarter(self) -> int: return math.ceil(self.month / 3) # String Formatting def to_date_string(self) -> str: """ Format the instance as date. :rtype: str """ return self.strftime("%Y-%m-%d") def to_formatted_date_string(self) -> str: """ Format the instance as a readable date. :rtype: str """ return self.strftime("%b %d, %Y") def __repr__(self) -> str: return f"{self.__class__.__name__}({self.year}, {self.month}, {self.day})" # COMPARISONS def closest(self, dt1: date, dt2: date) -> Self: """ Get the closest date from the instance. """ dt1 = self.__class__(dt1.year, dt1.month, dt1.day) dt2 = self.__class__(dt2.year, dt2.month, dt2.day) if self.diff(dt1).in_seconds() < self.diff(dt2).in_seconds(): return dt1 return dt2 def farthest(self, dt1: date, dt2: date) -> Self: """ Get the farthest date from the instance. """ dt1 = self.__class__(dt1.year, dt1.month, dt1.day) dt2 = self.__class__(dt2.year, dt2.month, dt2.day) if self.diff(dt1).in_seconds() > self.diff(dt2).in_seconds(): return dt1 return dt2 def is_future(self) -> bool: """ Determines if the instance is in the future, ie. greater than now. """ return self > self.today() def is_past(self) -> bool: """ Determines if the instance is in the past, ie. less than now. """ return self < self.today() def is_leap_year(self) -> bool: """ Determines if the instance is a leap year. """ return calendar.isleap(self.year) def is_long_year(self) -> bool: """ Determines if the instance is a long year See link ``_ """ return Date(self.year, 12, 28).isocalendar()[1] == 53 def is_same_day(self, dt: date) -> bool: """ Checks if the passed in date is the same day as the instance current day. """ return self == dt def is_anniversary(self, dt: date | None = None) -> bool: """ Check if it's the anniversary. Compares the date/month values of the two dates. """ if dt is None: dt = self.__class__.today() instance = self.__class__(dt.year, dt.month, dt.day) return (self.month, self.day) == (instance.month, instance.day) # the additional method for checking if today is the anniversary day # the alias is provided to start using a new name and keep the backward # compatibility the old name can be completely replaced with the new in # one of the future versions is_birthday = is_anniversary # ADDITIONS AND SUBTRACTIONS def add( self, years: int = 0, months: int = 0, weeks: int = 0, days: int = 0 ) -> Self: """ Add duration to the instance. :param years: The number of years :param months: The number of months :param weeks: The number of weeks :param days: The number of days """ dt = add_duration( date(self.year, self.month, self.day), years=years, months=months, weeks=weeks, days=days, ) return self.__class__(dt.year, dt.month, dt.day) def subtract( self, years: int = 0, months: int = 0, weeks: int = 0, days: int = 0 ) -> Self: """ Remove duration from the instance. :param years: The number of years :param months: The number of months :param weeks: The number of weeks :param days: The number of days """ return self.add(years=-years, months=-months, weeks=-weeks, days=-days) def _add_timedelta(self, delta: timedelta) -> Self: """ Add timedelta duration to the instance. :param delta: The timedelta instance """ if isinstance(delta, pendulum.Duration): return self.add( years=delta.years, months=delta.months, weeks=delta.weeks, days=delta.remaining_days, ) return self.add(days=delta.days) def _subtract_timedelta(self, delta: timedelta) -> Self: """ Remove timedelta duration from the instance. :param delta: The timedelta instance """ if isinstance(delta, pendulum.Duration): return self.subtract( years=delta.years, months=delta.months, weeks=delta.weeks, days=delta.remaining_days, ) return self.subtract(days=delta.days) def __add__(self, other: timedelta) -> Self: if not isinstance(other, timedelta): return NotImplemented return self._add_timedelta(other) @overload # type: ignore[override] # this is only needed because of Python 3.7 def __sub__(self, __delta: timedelta) -> Self: ... @overload def __sub__(self, __dt: datetime) -> NoReturn: ... @overload def __sub__(self, __dt: Self) -> Interval: ... def __sub__(self, other: timedelta | date) -> Self | Interval: if isinstance(other, timedelta): return self._subtract_timedelta(other) if not isinstance(other, date): return NotImplemented dt = self.__class__(other.year, other.month, other.day) return dt.diff(self, False) # DIFFERENCES def diff(self, dt: date | None = None, abs: bool = True) -> Interval: """ Returns the difference between two Date objects as an Interval. :param dt: The date to compare to (defaults to today) :param abs: Whether to return an absolute interval or not """ if dt is None: dt = self.today() return Interval(self, Date(dt.year, dt.month, dt.day), absolute=abs) def diff_for_humans( self, other: date | None = None, absolute: bool = False, locale: str | None = None, ) -> str: """ Get the difference in a human readable format in the current locale. When comparing a value in the past to default now: 1 day ago 5 months ago When comparing a value in the future to default now: 1 day from now 5 months from now When comparing a value in the past to another value: 1 day before 5 months before When comparing a value in the future to another value: 1 day after 5 months after :param other: The date to compare to (defaults to today) :param absolute: removes time difference modifiers ago, after, etc :param locale: The locale to use for localization """ is_now = other is None if is_now: other = self.today() diff = self.diff(other) return pendulum.format_diff(diff, is_now, absolute, locale) # MODIFIERS def start_of(self, unit: str) -> Self: """ Returns a copy of the instance with the time reset with the following rules: * day: time to 00:00:00 * week: date to first day of the week and time to 00:00:00 * month: date to first day of the month and time to 00:00:00 * year: date to first day of the year and time to 00:00:00 * decade: date to first day of the decade and time to 00:00:00 * century: date to first day of century and time to 00:00:00 :param unit: The unit to reset to """ if unit not in self._MODIFIERS_VALID_UNITS: raise ValueError(f'Invalid unit "{unit}" for start_of()') return cast("Self", getattr(self, f"_start_of_{unit}")()) def end_of(self, unit: str) -> Self: """ Returns a copy of the instance with the time reset with the following rules: * week: date to last day of the week * month: date to last day of the month * year: date to last day of the year * decade: date to last day of the decade * century: date to last day of century :param unit: The unit to reset to """ if unit not in self._MODIFIERS_VALID_UNITS: raise ValueError(f'Invalid unit "{unit}" for end_of()') return cast("Self", getattr(self, f"_end_of_{unit}")()) def _start_of_day(self) -> Self: """ Compatibility method. """ return self def _end_of_day(self) -> Self: """ Compatibility method """ return self def _start_of_month(self) -> Self: """ Reset the date to the first day of the month. """ return self.set(self.year, self.month, 1) def _end_of_month(self) -> Self: """ Reset the date to the last day of the month. """ return self.set(self.year, self.month, self.days_in_month) def _start_of_year(self) -> Self: """ Reset the date to the first day of the year. """ return self.set(self.year, 1, 1) def _end_of_year(self) -> Self: """ Reset the date to the last day of the year. """ return self.set(self.year, 12, 31) def _start_of_decade(self) -> Self: """ Reset the date to the first day of the decade. """ year = self.year - self.year % YEARS_PER_DECADE return self.set(year, 1, 1) def _end_of_decade(self) -> Self: """ Reset the date to the last day of the decade. """ year = self.year - self.year % YEARS_PER_DECADE + YEARS_PER_DECADE - 1 return self.set(year, 12, 31) def _start_of_century(self) -> Self: """ Reset the date to the first day of the century. """ year = self.year - 1 - (self.year - 1) % YEARS_PER_CENTURY + 1 return self.set(year, 1, 1) def _end_of_century(self) -> Self: """ Reset the date to the last day of the century. """ year = self.year - 1 - (self.year - 1) % YEARS_PER_CENTURY + YEARS_PER_CENTURY return self.set(year, 12, 31) def _start_of_week(self) -> Self: """ Reset the date to the first day of the week. """ dt = self if self.day_of_week != pendulum._WEEK_STARTS_AT: dt = self.previous(pendulum._WEEK_STARTS_AT) return dt.start_of("day") def _end_of_week(self) -> Self: """ Reset the date to the last day of the week. """ dt = self if self.day_of_week != pendulum._WEEK_ENDS_AT: dt = self.next(pendulum._WEEK_ENDS_AT) return dt.end_of("day") def next(self, day_of_week: WeekDay | None = None) -> Self: """ Modify to the next occurrence of a given day of the week. If no day_of_week is provided, modify to the next occurrence of the current day of the week. Use the supplied consts to indicate the desired day_of_week, ex. pendulum.MONDAY. :param day_of_week: The next day of week to reset to. """ if day_of_week is None: day_of_week = self.day_of_week if day_of_week < WeekDay.MONDAY or day_of_week > WeekDay.SUNDAY: raise ValueError("Invalid day of week") dt = self.add(days=1) while dt.day_of_week != day_of_week: dt = dt.add(days=1) return dt def previous(self, day_of_week: WeekDay | None = None) -> Self: """ Modify to the previous occurrence of a given day of the week. If no day_of_week is provided, modify to the previous occurrence of the current day of the week. Use the supplied consts to indicate the desired day_of_week, ex. pendulum.MONDAY. :param day_of_week: The previous day of week to reset to. """ if day_of_week is None: day_of_week = self.day_of_week if day_of_week < WeekDay.MONDAY or day_of_week > WeekDay.SUNDAY: raise ValueError("Invalid day of week") dt = self.subtract(days=1) while dt.day_of_week != day_of_week: dt = dt.subtract(days=1) return dt def first_of(self, unit: str, day_of_week: WeekDay | None = None) -> Self: """ Returns an instance set to the first occurrence of a given day of the week in the current unit. If no day_of_week is provided, modify to the first day of the unit. Use the supplied consts to indicate the desired day_of_week, ex. pendulum.MONDAY. Supported units are month, quarter and year. :param unit: The unit to use :param day_of_week: The day of week to reset to. """ if unit not in ["month", "quarter", "year"]: raise ValueError(f'Invalid unit "{unit}" for first_of()') return cast("Self", getattr(self, f"_first_of_{unit}")(day_of_week)) def last_of(self, unit: str, day_of_week: WeekDay | None = None) -> Self: """ Returns an instance set to the last occurrence of a given day of the week in the current unit. If no day_of_week is provided, modify to the last day of the unit. Use the supplied consts to indicate the desired day_of_week, ex. pendulum.MONDAY. Supported units are month, quarter and year. :param unit: The unit to use :param day_of_week: The day of week to reset to. """ if unit not in ["month", "quarter", "year"]: raise ValueError(f'Invalid unit "{unit}" for first_of()') return cast("Self", getattr(self, f"_last_of_{unit}")(day_of_week)) def nth_of(self, unit: str, nth: int, day_of_week: WeekDay) -> Self: """ Returns a new instance set to the given occurrence of a given day of the week in the current unit. If the calculated occurrence is outside the scope of the current unit, then raise an error. Use the supplied consts to indicate the desired day_of_week, ex. pendulum.MONDAY. Supported units are month, quarter and year. :param unit: The unit to use :param nth: The occurrence to use :param day_of_week: The day of week to set to. """ if unit not in ["month", "quarter", "year"]: raise ValueError(f'Invalid unit "{unit}" for first_of()') dt = cast("Self", getattr(self, f"_nth_of_{unit}")(nth, day_of_week)) if not dt: raise PendulumException( f"Unable to find occurrence {nth}" f" of {WeekDay(day_of_week).name.capitalize()} in {unit}" ) return dt def _first_of_month(self, day_of_week: WeekDay) -> Self: """ Modify to the first occurrence of a given day of the week in the current month. If no day_of_week is provided, modify to the first day of the month. Use the supplied consts to indicate the desired day_of_week, ex. pendulum.MONDAY. :param day_of_week: The day of week to set to. """ dt = self if day_of_week is None: return dt.set(day=1) month = calendar.monthcalendar(dt.year, dt.month) calendar_day = day_of_week if month[0][calendar_day] > 0: day_of_month = month[0][calendar_day] else: day_of_month = month[1][calendar_day] return dt.set(day=day_of_month) def _last_of_month(self, day_of_week: WeekDay | None = None) -> Self: """ Modify to the last occurrence of a given day of the week in the current month. If no day_of_week is provided, modify to the last day of the month. Use the supplied consts to indicate the desired day_of_week, ex. pendulum.MONDAY. :param day_of_week: The day of week to set to. """ dt = self if day_of_week is None: return dt.set(day=self.days_in_month) month = calendar.monthcalendar(dt.year, dt.month) calendar_day = day_of_week if month[-1][calendar_day] > 0: day_of_month = month[-1][calendar_day] else: day_of_month = month[-2][calendar_day] return dt.set(day=day_of_month) def _nth_of_month(self, nth: int, day_of_week: WeekDay) -> Self | None: """ Modify to the given occurrence of a given day of the week in the current month. If the calculated occurrence is outside, the scope of the current month, then return False and no modifications are made. Use the supplied consts to indicate the desired day_of_week, ex. pendulum.MONDAY. """ if nth == 1: return self.first_of("month", day_of_week) dt = self.first_of("month") check = dt.format("YYYY-MM") for _ in range(nth - (1 if dt.day_of_week == day_of_week else 0)): dt = dt.next(day_of_week) if dt.format("YYYY-MM") == check: return self.set(day=dt.day) return None def _first_of_quarter(self, day_of_week: WeekDay | None = None) -> Self: """ Modify to the first occurrence of a given day of the week in the current quarter. If no day_of_week is provided, modify to the first day of the quarter. Use the supplied consts to indicate the desired day_of_week, ex. pendulum.MONDAY. """ return self.set(self.year, self.quarter * 3 - 2, 1).first_of( "month", day_of_week ) def _last_of_quarter(self, day_of_week: WeekDay | None = None) -> Self: """ Modify to the last occurrence of a given day of the week in the current quarter. If no day_of_week is provided, modify to the last day of the quarter. Use the supplied consts to indicate the desired day_of_week, ex. pendulum.MONDAY. """ return self.set(self.year, self.quarter * 3, 1).last_of("month", day_of_week) def _nth_of_quarter(self, nth: int, day_of_week: WeekDay) -> Self | None: """ Modify to the given occurrence of a given day of the week in the current quarter. If the calculated occurrence is outside, the scope of the current quarter, then return False and no modifications are made. Use the supplied consts to indicate the desired day_of_week, ex. pendulum.MONDAY. """ if nth == 1: return self.first_of("quarter", day_of_week) dt = self.replace(self.year, self.quarter * 3, 1) last_month = dt.month year = dt.year dt = dt.first_of("quarter") for _ in range(nth - (1 if dt.day_of_week == day_of_week else 0)): dt = dt.next(day_of_week) if last_month < dt.month or year != dt.year: return None return self.set(self.year, dt.month, dt.day) def _first_of_year(self, day_of_week: WeekDay | None = None) -> Self: """ Modify to the first occurrence of a given day of the week in the current year. If no day_of_week is provided, modify to the first day of the year. Use the supplied consts to indicate the desired day_of_week, ex. pendulum.MONDAY. """ return self.set(month=1).first_of("month", day_of_week) def _last_of_year(self, day_of_week: WeekDay | None = None) -> Self: """ Modify to the last occurrence of a given day of the week in the current year. If no day_of_week is provided, modify to the last day of the year. Use the supplied consts to indicate the desired day_of_week, ex. pendulum.MONDAY. """ return self.set(month=MONTHS_PER_YEAR).last_of("month", day_of_week) def _nth_of_year(self, nth: int, day_of_week: WeekDay) -> Self | None: """ Modify to the given occurrence of a given day of the week in the current year. If the calculated occurrence is outside, the scope of the current year, then return False and no modifications are made. Use the supplied consts to indicate the desired day_of_week, ex. pendulum.MONDAY. """ if nth == 1: return self.first_of("year", day_of_week) dt = self.first_of("year") year = dt.year for _ in range(nth - (1 if dt.day_of_week == day_of_week else 0)): dt = dt.next(day_of_week) if year != dt.year: return None return self.set(self.year, dt.month, dt.day) def average(self, dt: date | None = None) -> Self: """ Modify the current instance to the average of a given instance (default now) and the current instance. """ if dt is None: dt = Date.today() return self.add(days=int(self.diff(dt, False).in_days() / 2)) # Native methods override @classmethod def today(cls) -> Self: dt = date.today() return cls(dt.year, dt.month, dt.day) @classmethod def fromtimestamp(cls, t: float) -> Self: dt = super().fromtimestamp(t) return cls(dt.year, dt.month, dt.day) @classmethod def fromordinal(cls, n: int) -> Self: dt = super().fromordinal(n) return cls(dt.year, dt.month, dt.day) def replace( self, year: SupportsIndex | None = None, month: SupportsIndex | None = None, day: SupportsIndex | None = None, ) -> Self: year = year if year is not None else self.year month = month if month is not None else self.month day = day if day is not None else self.day return self.__class__(year, month, day) pendulum-3.0.0/src/pendulum/datetime.py000066400000000000000000001225051453741033500201430ustar00rootroot00000000000000from __future__ import annotations import calendar import datetime import traceback from typing import TYPE_CHECKING from typing import Any from typing import Callable from typing import ClassVar from typing import Optional from typing import cast from typing import overload import pendulum from pendulum.constants import ATOM from pendulum.constants import COOKIE from pendulum.constants import MINUTES_PER_HOUR from pendulum.constants import MONTHS_PER_YEAR from pendulum.constants import RFC822 from pendulum.constants import RFC850 from pendulum.constants import RFC1036 from pendulum.constants import RFC1123 from pendulum.constants import RFC2822 from pendulum.constants import RSS from pendulum.constants import SECONDS_PER_DAY from pendulum.constants import SECONDS_PER_MINUTE from pendulum.constants import W3C from pendulum.constants import YEARS_PER_CENTURY from pendulum.constants import YEARS_PER_DECADE from pendulum.date import Date from pendulum.day import WeekDay from pendulum.exceptions import PendulumException from pendulum.helpers import add_duration from pendulum.interval import Interval from pendulum.time import Time from pendulum.tz import UTC from pendulum.tz import local_timezone from pendulum.tz.timezone import FixedTimezone from pendulum.tz.timezone import Timezone if TYPE_CHECKING: from typing_extensions import Literal from typing_extensions import Self from typing_extensions import SupportsIndex class DateTime(datetime.datetime, Date): EPOCH: ClassVar[DateTime] min: ClassVar[DateTime] max: ClassVar[DateTime] # Formats _FORMATS: ClassVar[dict[str, str | Callable[[datetime.datetime], str]]] = { "atom": ATOM, "cookie": COOKIE, "iso8601": lambda dt: dt.isoformat("T"), "rfc822": RFC822, "rfc850": RFC850, "rfc1036": RFC1036, "rfc1123": RFC1123, "rfc2822": RFC2822, "rfc3339": lambda dt: dt.isoformat("T"), "rss": RSS, "w3c": W3C, } _MODIFIERS_VALID_UNITS: ClassVar[list[str]] = [ "second", "minute", "hour", "day", "week", "month", "year", "decade", "century", ] _EPOCH: datetime.datetime = datetime.datetime(1970, 1, 1, tzinfo=UTC) @classmethod def create( cls, year: SupportsIndex, month: SupportsIndex, day: SupportsIndex, hour: SupportsIndex = 0, minute: SupportsIndex = 0, second: SupportsIndex = 0, microsecond: SupportsIndex = 0, tz: str | float | Timezone | FixedTimezone | None | datetime.tzinfo = UTC, fold: int = 1, raise_on_unknown_times: bool = False, ) -> Self: """ Creates a new DateTime instance from a specific date and time. """ if tz is not None: tz = pendulum._safe_timezone(tz) dt = datetime.datetime( year, month, day, hour, minute, second, microsecond, fold=fold ) if tz is not None: dt = tz.convert(dt, raise_on_unknown_times=raise_on_unknown_times) return cls( dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, dt.microsecond, tzinfo=dt.tzinfo, fold=dt.fold, ) @classmethod def instance( cls, dt: datetime.datetime, tz: str | Timezone | FixedTimezone | datetime.tzinfo | None = UTC, ) -> Self: tz = dt.tzinfo or tz if tz is not None: tz = pendulum._safe_timezone(tz, dt=dt) return cls.create( dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, dt.microsecond, tz=tz, fold=dt.fold, ) @overload @classmethod def now(cls, tz: datetime.tzinfo | None = None) -> Self: ... @overload @classmethod def now(cls, tz: str | Timezone | FixedTimezone | None = None) -> Self: ... @classmethod def now( cls, tz: str | Timezone | FixedTimezone | datetime.tzinfo | None = None ) -> Self: """ Get a DateTime instance for the current date and time. """ if tz is None or tz == "local": dt = datetime.datetime.now(local_timezone()) elif tz is UTC or tz == "UTC": dt = datetime.datetime.now(UTC) else: dt = datetime.datetime.now(UTC) tz = pendulum._safe_timezone(tz) dt = dt.astimezone(tz) return cls( dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, dt.microsecond, tzinfo=dt.tzinfo, fold=dt.fold, ) @classmethod def utcnow(cls) -> Self: """ Get a DateTime instance for the current date and time in UTC. """ return cls.now(UTC) @classmethod def today(cls) -> Self: return cls.now() @classmethod def strptime(cls, time: str, fmt: str) -> Self: return cls.instance(datetime.datetime.strptime(time, fmt)) # Getters/Setters def set( self, year: int | None = None, month: int | None = None, day: int | None = None, hour: int | None = None, minute: int | None = None, second: int | None = None, microsecond: int | None = None, tz: str | float | Timezone | FixedTimezone | datetime.tzinfo | None = None, ) -> Self: if year is None: year = self.year if month is None: month = self.month if day is None: day = self.day if hour is None: hour = self.hour if minute is None: minute = self.minute if second is None: second = self.second if microsecond is None: microsecond = self.microsecond if tz is None: tz = self.tz return self.__class__.create( year, month, day, hour, minute, second, microsecond, tz=tz, fold=self.fold ) @property def float_timestamp(self) -> float: return self.timestamp() @property def int_timestamp(self) -> int: # Workaround needed to avoid inaccuracy # for far into the future datetimes dt = datetime.datetime( self.year, self.month, self.day, self.hour, self.minute, self.second, self.microsecond, tzinfo=self.tzinfo, fold=self.fold, ) delta = dt - self._EPOCH return delta.days * SECONDS_PER_DAY + delta.seconds @property def offset(self) -> int | None: return self.get_offset() @property def offset_hours(self) -> float | None: offset = self.get_offset() if offset is None: return None return offset / SECONDS_PER_MINUTE / MINUTES_PER_HOUR @property def timezone(self) -> Timezone | FixedTimezone | None: if not isinstance(self.tzinfo, (Timezone, FixedTimezone)): return None return self.tzinfo @property def tz(self) -> Timezone | FixedTimezone | None: return self.timezone @property def timezone_name(self) -> str | None: tz = self.timezone if tz is None: return None return tz.name @property def age(self) -> int: return self.date().diff(self.now(self.tz).date(), abs=False).in_years() def is_local(self) -> bool: return self.offset == self.in_timezone(pendulum.local_timezone()).offset def is_utc(self) -> bool: return self.offset == 0 def is_dst(self) -> bool: return self.dst() != datetime.timedelta() def get_offset(self) -> int | None: utcoffset = self.utcoffset() if utcoffset is None: return None return int(utcoffset.total_seconds()) def date(self) -> Date: return Date(self.year, self.month, self.day) def time(self) -> Time: return Time(self.hour, self.minute, self.second, self.microsecond) def naive(self) -> Self: """ Return the DateTime without timezone information. """ return self.__class__( self.year, self.month, self.day, self.hour, self.minute, self.second, self.microsecond, ) def on(self, year: int, month: int, day: int) -> Self: """ Returns a new instance with the current date set to a different date. """ return self.set(year=int(year), month=int(month), day=int(day)) def at( self, hour: int, minute: int = 0, second: int = 0, microsecond: int = 0 ) -> Self: """ Returns a new instance with the current time to a different time. """ return self.set( hour=hour, minute=minute, second=second, microsecond=microsecond ) def in_timezone(self, tz: str | Timezone | FixedTimezone) -> Self: """ Set the instance's timezone from a string or object. """ tz = pendulum._safe_timezone(tz) dt = self if not self.timezone: dt = dt.replace(fold=1) return tz.convert(dt) def in_tz(self, tz: str | Timezone | FixedTimezone) -> Self: """ Set the instance's timezone from a string or object. """ return self.in_timezone(tz) # STRING FORMATTING def to_time_string(self) -> str: """ Format the instance as time. """ return self.format("HH:mm:ss") def to_datetime_string(self) -> str: """ Format the instance as date and time. """ return self.format("YYYY-MM-DD HH:mm:ss") def to_day_datetime_string(self) -> str: """ Format the instance as day, date and time (in english). """ return self.format("ddd, MMM D, YYYY h:mm A", locale="en") def to_atom_string(self) -> str: """ Format the instance as ATOM. """ return self._to_string("atom") def to_cookie_string(self) -> str: """ Format the instance as COOKIE. """ return self._to_string("cookie", locale="en") def to_iso8601_string(self) -> str: """ Format the instance as ISO 8601. """ string = self._to_string("iso8601") if self.tz and self.tz.name == "UTC": string = string.replace("+00:00", "Z") return string def to_rfc822_string(self) -> str: """ Format the instance as RFC 822. """ return self._to_string("rfc822") def to_rfc850_string(self) -> str: """ Format the instance as RFC 850. """ return self._to_string("rfc850") def to_rfc1036_string(self) -> str: """ Format the instance as RFC 1036. """ return self._to_string("rfc1036") def to_rfc1123_string(self) -> str: """ Format the instance as RFC 1123. """ return self._to_string("rfc1123") def to_rfc2822_string(self) -> str: """ Format the instance as RFC 2822. """ return self._to_string("rfc2822") def to_rfc3339_string(self) -> str: """ Format the instance as RFC 3339. """ return self._to_string("rfc3339") def to_rss_string(self) -> str: """ Format the instance as RSS. """ return self._to_string("rss") def to_w3c_string(self) -> str: """ Format the instance as W3C. """ return self._to_string("w3c") def _to_string(self, fmt: str, locale: str | None = None) -> str: """ Format the instance to a common string format. """ if fmt not in self._FORMATS: raise ValueError(f"Format [{fmt}] is not supported") fmt_value = self._FORMATS[fmt] if callable(fmt_value): return fmt_value(self) return self.format(fmt_value, locale=locale) def __str__(self) -> str: return self.isoformat(" ") def __repr__(self) -> str: us = "" if self.microsecond: us = f", {self.microsecond}" repr_ = "{klass}(" "{year}, {month}, {day}, " "{hour}, {minute}, {second}{us}" if self.tzinfo is not None: repr_ += ", tzinfo={tzinfo}" repr_ += ")" return repr_.format( klass=self.__class__.__name__, year=self.year, month=self.month, day=self.day, hour=self.hour, minute=self.minute, second=self.second, us=us, tzinfo=repr(self.tzinfo), ) # Comparisons def closest(self, *dts: datetime.datetime) -> Self: # type: ignore[override] """ Get the closest date to the instance. """ pdts = [self.instance(x) for x in dts] return min((abs(self - dt), dt) for dt in pdts)[1] def farthest(self, *dts: datetime.datetime) -> Self: # type: ignore[override] """ Get the farthest date from the instance. """ pdts = [self.instance(x) for x in dts] return max((abs(self - dt), dt) for dt in pdts)[1] def is_future(self) -> bool: """ Determines if the instance is in the future, ie. greater than now. """ return self > self.now(self.timezone) def is_past(self) -> bool: """ Determines if the instance is in the past, ie. less than now. """ return self < self.now(self.timezone) def is_long_year(self) -> bool: """ Determines if the instance is a long year See link `https://en.wikipedia.org/wiki/ISO_8601#Week_dates`_ """ return ( DateTime.create(self.year, 12, 28, 0, 0, 0, tz=self.tz).isocalendar()[1] == 53 ) def is_same_day(self, dt: datetime.datetime) -> bool: # type: ignore[override] """ Checks if the passed in date is the same day as the instance current day. """ dt = self.instance(dt) return self.to_date_string() == dt.to_date_string() def is_anniversary( # type: ignore[override] self, dt: datetime.datetime | None = None ) -> bool: """ Check if its the anniversary. Compares the date/month values of the two dates. """ if dt is None: dt = self.now(self.tz) instance = self.instance(dt) return (self.month, self.day) == (instance.month, instance.day) # ADDITIONS AND SUBSTRACTIONS def add( self, years: int = 0, months: int = 0, weeks: int = 0, days: int = 0, hours: int = 0, minutes: int = 0, seconds: float = 0, microseconds: int = 0, ) -> Self: """ Add a duration to the instance. If we're adding units of variable length (i.e., years, months), move forward from current time, otherwise move forward from utc, for accuracy when moving across DST boundaries. """ units_of_variable_length = any([years, months, weeks, days]) current_dt = datetime.datetime( self.year, self.month, self.day, self.hour, self.minute, self.second, self.microsecond, ) if not units_of_variable_length: offset = self.utcoffset() if offset: current_dt = current_dt - offset dt = add_duration( current_dt, years=years, months=months, weeks=weeks, days=days, hours=hours, minutes=minutes, seconds=seconds, microseconds=microseconds, ) if units_of_variable_length or self.tz is None: return self.__class__.create( dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, dt.microsecond, tz=self.tz, ) dt = datetime.datetime( dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, dt.microsecond, tzinfo=UTC, ) dt = self.tz.convert(dt) return self.__class__( dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, dt.microsecond, tzinfo=self.tz, fold=dt.fold, ) def subtract( self, years: int = 0, months: int = 0, weeks: int = 0, days: int = 0, hours: int = 0, minutes: int = 0, seconds: float = 0, microseconds: int = 0, ) -> Self: """ Remove duration from the instance. """ return self.add( years=-years, months=-months, weeks=-weeks, days=-days, hours=-hours, minutes=-minutes, seconds=-seconds, microseconds=-microseconds, ) # Adding a final underscore to the method name # to avoid errors for PyPy which already defines # a _add_timedelta method def _add_timedelta_(self, delta: datetime.timedelta) -> Self: """ Add timedelta duration to the instance. """ if isinstance(delta, pendulum.Interval): return self.add( years=delta.years, months=delta.months, weeks=delta.weeks, days=delta.remaining_days, hours=delta.hours, minutes=delta.minutes, seconds=delta.remaining_seconds, microseconds=delta.microseconds, ) elif isinstance(delta, pendulum.Duration): return self.add(**delta._signature) # type: ignore[attr-defined] return self.add(seconds=delta.total_seconds()) def _subtract_timedelta(self, delta: datetime.timedelta) -> Self: """ Remove timedelta duration from the instance. """ if isinstance(delta, pendulum.Duration): return self.subtract( years=delta.years, months=delta.months, seconds=delta._total ) return self.subtract(seconds=delta.total_seconds()) # DIFFERENCES def diff( # type: ignore[override] self, dt: datetime.datetime | None = None, abs: bool = True ) -> Interval: """ Returns the difference between two DateTime objects represented as an Interval. """ if dt is None: dt = self.now(self.tz) return Interval(self, dt, absolute=abs) def diff_for_humans( # type: ignore[override] self, other: DateTime | None = None, absolute: bool = False, locale: str | None = None, ) -> str: """ Get the difference in a human readable format in the current locale. When comparing a value in the past to default now: 1 day ago 5 months ago When comparing a value in the future to default now: 1 day from now 5 months from now When comparing a value in the past to another value: 1 day before 5 months before When comparing a value in the future to another value: 1 day after 5 months after """ is_now = other is None if is_now: other = self.now() diff = self.diff(other) return pendulum.format_diff(diff, is_now, absolute, locale) # Modifiers def start_of(self, unit: str) -> Self: """ Returns a copy of the instance with the time reset with the following rules: * second: microsecond set to 0 * minute: second and microsecond set to 0 * hour: minute, second and microsecond set to 0 * day: time to 00:00:00 * week: date to first day of the week and time to 00:00:00 * month: date to first day of the month and time to 00:00:00 * year: date to first day of the year and time to 00:00:00 * decade: date to first day of the decade and time to 00:00:00 * century: date to first day of century and time to 00:00:00 """ if unit not in self._MODIFIERS_VALID_UNITS: raise ValueError(f'Invalid unit "{unit}" for start_of()') return cast("Self", getattr(self, f"_start_of_{unit}")()) def end_of(self, unit: str) -> Self: """ Returns a copy of the instance with the time reset with the following rules: * second: microsecond set to 999999 * minute: second set to 59 and microsecond set to 999999 * hour: minute and second set to 59 and microsecond set to 999999 * day: time to 23:59:59.999999 * week: date to last day of the week and time to 23:59:59.999999 * month: date to last day of the month and time to 23:59:59.999999 * year: date to last day of the year and time to 23:59:59.999999 * decade: date to last day of the decade and time to 23:59:59.999999 * century: date to last day of century and time to 23:59:59.999999 """ if unit not in self._MODIFIERS_VALID_UNITS: raise ValueError(f'Invalid unit "{unit}" for end_of()') return cast("Self", getattr(self, f"_end_of_{unit}")()) def _start_of_second(self) -> Self: """ Reset microseconds to 0. """ return self.set(microsecond=0) def _end_of_second(self) -> Self: """ Set microseconds to 999999. """ return self.set(microsecond=999999) def _start_of_minute(self) -> Self: """ Reset seconds and microseconds to 0. """ return self.set(second=0, microsecond=0) def _end_of_minute(self) -> Self: """ Set seconds to 59 and microseconds to 999999. """ return self.set(second=59, microsecond=999999) def _start_of_hour(self) -> Self: """ Reset minutes, seconds and microseconds to 0. """ return self.set(minute=0, second=0, microsecond=0) def _end_of_hour(self) -> Self: """ Set minutes and seconds to 59 and microseconds to 999999. """ return self.set(minute=59, second=59, microsecond=999999) def _start_of_day(self) -> Self: """ Reset the time to 00:00:00. """ return self.at(0, 0, 0, 0) def _end_of_day(self) -> Self: """ Reset the time to 23:59:59.999999. """ return self.at(23, 59, 59, 999999) def _start_of_month(self) -> Self: """ Reset the date to the first day of the month and the time to 00:00:00. """ return self.set(self.year, self.month, 1, 0, 0, 0, 0) def _end_of_month(self) -> Self: """ Reset the date to the last day of the month and the time to 23:59:59.999999. """ return self.set(self.year, self.month, self.days_in_month, 23, 59, 59, 999999) def _start_of_year(self) -> Self: """ Reset the date to the first day of the year and the time to 00:00:00. """ return self.set(self.year, 1, 1, 0, 0, 0, 0) def _end_of_year(self) -> Self: """ Reset the date to the last day of the year and the time to 23:59:59.999999. """ return self.set(self.year, 12, 31, 23, 59, 59, 999999) def _start_of_decade(self) -> Self: """ Reset the date to the first day of the decade and the time to 00:00:00. """ year = self.year - self.year % YEARS_PER_DECADE return self.set(year, 1, 1, 0, 0, 0, 0) def _end_of_decade(self) -> Self: """ Reset the date to the last day of the decade and the time to 23:59:59.999999. """ year = self.year - self.year % YEARS_PER_DECADE + YEARS_PER_DECADE - 1 return self.set(year, 12, 31, 23, 59, 59, 999999) def _start_of_century(self) -> Self: """ Reset the date to the first day of the century and the time to 00:00:00. """ year = self.year - 1 - (self.year - 1) % YEARS_PER_CENTURY + 1 return self.set(year, 1, 1, 0, 0, 0, 0) def _end_of_century(self) -> Self: """ Reset the date to the last day of the century and the time to 23:59:59.999999. """ year = self.year - 1 - (self.year - 1) % YEARS_PER_CENTURY + YEARS_PER_CENTURY return self.set(year, 12, 31, 23, 59, 59, 999999) def _start_of_week(self) -> Self: """ Reset the date to the first day of the week and the time to 00:00:00. """ dt = self if self.day_of_week != pendulum._WEEK_STARTS_AT: dt = self.previous(pendulum._WEEK_STARTS_AT) return dt.start_of("day") def _end_of_week(self) -> Self: """ Reset the date to the last day of the week and the time to 23:59:59. """ dt = self if self.day_of_week != pendulum._WEEK_ENDS_AT: dt = self.next(pendulum._WEEK_ENDS_AT) return dt.end_of("day") def next(self, day_of_week: WeekDay | None = None, keep_time: bool = False) -> Self: """ Modify to the next occurrence of a given day of the week. If no day_of_week is provided, modify to the next occurrence of the current day of the week. Use the supplied consts to indicate the desired day_of_week, ex. DateTime.MONDAY. """ if day_of_week is None: day_of_week = self.day_of_week if day_of_week < WeekDay.MONDAY or day_of_week > WeekDay.SUNDAY: raise ValueError("Invalid day of week") dt = self if keep_time else self.start_of("day") dt = dt.add(days=1) while dt.day_of_week != day_of_week: dt = dt.add(days=1) return dt def previous( self, day_of_week: WeekDay | None = None, keep_time: bool = False ) -> Self: """ Modify to the previous occurrence of a given day of the week. If no day_of_week is provided, modify to the previous occurrence of the current day of the week. Use the supplied consts to indicate the desired day_of_week, ex. DateTime.MONDAY. """ if day_of_week is None: day_of_week = self.day_of_week if day_of_week < WeekDay.MONDAY or day_of_week > WeekDay.SUNDAY: raise ValueError("Invalid day of week") dt = self if keep_time else self.start_of("day") dt = dt.subtract(days=1) while dt.day_of_week != day_of_week: dt = dt.subtract(days=1) return dt def first_of(self, unit: str, day_of_week: WeekDay | None = None) -> Self: """ Returns an instance set to the first occurrence of a given day of the week in the current unit. If no day_of_week is provided, modify to the first day of the unit. Use the supplied consts to indicate the desired day_of_week, ex. DateTime.MONDAY. Supported units are month, quarter and year. """ if unit not in ["month", "quarter", "year"]: raise ValueError(f'Invalid unit "{unit}" for first_of()') return cast("Self", getattr(self, f"_first_of_{unit}")(day_of_week)) def last_of(self, unit: str, day_of_week: WeekDay | None = None) -> Self: """ Returns an instance set to the last occurrence of a given day of the week in the current unit. If no day_of_week is provided, modify to the last day of the unit. Use the supplied consts to indicate the desired day_of_week, ex. DateTime.MONDAY. Supported units are month, quarter and year. """ if unit not in ["month", "quarter", "year"]: raise ValueError(f'Invalid unit "{unit}" for first_of()') return cast("Self", getattr(self, f"_last_of_{unit}")(day_of_week)) def nth_of(self, unit: str, nth: int, day_of_week: WeekDay) -> Self: """ Returns a new instance set to the given occurrence of a given day of the week in the current unit. If the calculated occurrence is outside the scope of the current unit, then raise an error. Use the supplied consts to indicate the desired day_of_week, ex. DateTime.MONDAY. Supported units are month, quarter and year. """ if unit not in ["month", "quarter", "year"]: raise ValueError(f'Invalid unit "{unit}" for first_of()') dt = cast(Optional["Self"], getattr(self, f"_nth_of_{unit}")(nth, day_of_week)) if not dt: raise PendulumException( f"Unable to find occurrence {nth}" f" of {WeekDay(day_of_week).name.capitalize()} in {unit}" ) return dt def _first_of_month(self, day_of_week: WeekDay | None = None) -> Self: """ Modify to the first occurrence of a given day of the week in the current month. If no day_of_week is provided, modify to the first day of the month. Use the supplied consts to indicate the desired day_of_week, ex. DateTime.MONDAY. """ dt = self.start_of("day") if day_of_week is None: return dt.set(day=1) month = calendar.monthcalendar(dt.year, dt.month) calendar_day = day_of_week if month[0][calendar_day] > 0: day_of_month = month[0][calendar_day] else: day_of_month = month[1][calendar_day] return dt.set(day=day_of_month) def _last_of_month(self, day_of_week: WeekDay | None = None) -> Self: """ Modify to the last occurrence of a given day of the week in the current month. If no day_of_week is provided, modify to the last day of the month. Use the supplied consts to indicate the desired day_of_week, ex. DateTime.MONDAY. """ dt = self.start_of("day") if day_of_week is None: return dt.set(day=self.days_in_month) month = calendar.monthcalendar(dt.year, dt.month) calendar_day = day_of_week if month[-1][calendar_day] > 0: day_of_month = month[-1][calendar_day] else: day_of_month = month[-2][calendar_day] return dt.set(day=day_of_month) def _nth_of_month( self, nth: int, day_of_week: WeekDay | None = None ) -> Self | None: """ Modify to the given occurrence of a given day of the week in the current month. If the calculated occurrence is outside, the scope of the current month, then return False and no modifications are made. Use the supplied consts to indicate the desired day_of_week, ex. DateTime.MONDAY. """ if nth == 1: return self.first_of("month", day_of_week) dt = self.first_of("month") check = dt.format("%Y-%M") for _ in range(nth - (1 if dt.day_of_week == day_of_week else 0)): dt = dt.next(day_of_week) if dt.format("%Y-%M") == check: return self.set(day=dt.day).start_of("day") return None def _first_of_quarter(self, day_of_week: WeekDay | None = None) -> Self: """ Modify to the first occurrence of a given day of the week in the current quarter. If no day_of_week is provided, modify to the first day of the quarter. Use the supplied consts to indicate the desired day_of_week, ex. DateTime.MONDAY. """ return self.on(self.year, self.quarter * 3 - 2, 1).first_of( "month", day_of_week ) def _last_of_quarter(self, day_of_week: WeekDay | None = None) -> Self: """ Modify to the last occurrence of a given day of the week in the current quarter. If no day_of_week is provided, modify to the last day of the quarter. Use the supplied consts to indicate the desired day_of_week, ex. DateTime.MONDAY. """ return self.on(self.year, self.quarter * 3, 1).last_of("month", day_of_week) def _nth_of_quarter( self, nth: int, day_of_week: WeekDay | None = None ) -> Self | None: """ Modify to the given occurrence of a given day of the week in the current quarter. If the calculated occurrence is outside, the scope of the current quarter, then return False and no modifications are made. Use the supplied consts to indicate the desired day_of_week, ex. DateTime.MONDAY. """ if nth == 1: return self.first_of("quarter", day_of_week) dt = self.set(day=1, month=self.quarter * 3) last_month = dt.month year = dt.year dt = dt.first_of("quarter") for _ in range(nth - (1 if dt.day_of_week == day_of_week else 0)): dt = dt.next(day_of_week) if last_month < dt.month or year != dt.year: return None return self.on(self.year, dt.month, dt.day).start_of("day") def _first_of_year(self, day_of_week: WeekDay | None = None) -> Self: """ Modify to the first occurrence of a given day of the week in the current year. If no day_of_week is provided, modify to the first day of the year. Use the supplied consts to indicate the desired day_of_week, ex. DateTime.MONDAY. """ return self.set(month=1).first_of("month", day_of_week) def _last_of_year(self, day_of_week: WeekDay | None = None) -> Self: """ Modify to the last occurrence of a given day of the week in the current year. If no day_of_week is provided, modify to the last day of the year. Use the supplied consts to indicate the desired day_of_week, ex. DateTime.MONDAY. """ return self.set(month=MONTHS_PER_YEAR).last_of("month", day_of_week) def _nth_of_year(self, nth: int, day_of_week: WeekDay | None = None) -> Self | None: """ Modify to the given occurrence of a given day of the week in the current year. If the calculated occurrence is outside, the scope of the current year, then return False and no modifications are made. Use the supplied consts to indicate the desired day_of_week, ex. DateTime.MONDAY. """ if nth == 1: return self.first_of("year", day_of_week) dt = self.first_of("year") year = dt.year for _ in range(nth - (1 if dt.day_of_week == day_of_week else 0)): dt = dt.next(day_of_week) if year != dt.year: return None return self.on(self.year, dt.month, dt.day).start_of("day") def average( # type: ignore[override] self, dt: datetime.datetime | None = None ) -> Self: """ Modify the current instance to the average of a given instance (default now) and the current instance. """ if dt is None: dt = self.now(self.tz) diff = self.diff(dt, False) return self.add( microseconds=(diff.in_seconds() * 1000000 + diff.microseconds) // 2 ) @overload # type: ignore[override] def __sub__(self, other: datetime.timedelta) -> Self: ... @overload def __sub__(self, other: DateTime) -> Interval: ... def __sub__(self, other: datetime.datetime | datetime.timedelta) -> Self | Interval: if isinstance(other, datetime.timedelta): return self._subtract_timedelta(other) if not isinstance(other, datetime.datetime): return NotImplemented if not isinstance(other, self.__class__): if other.tzinfo is None: other = pendulum.naive( other.year, other.month, other.day, other.hour, other.minute, other.second, other.microsecond, ) else: other = self.instance(other) return other.diff(self, False) def __rsub__(self, other: datetime.datetime) -> Interval: if not isinstance(other, datetime.datetime): return NotImplemented if not isinstance(other, self.__class__): if other.tzinfo is None: other = pendulum.naive( other.year, other.month, other.day, other.hour, other.minute, other.second, other.microsecond, ) else: other = self.instance(other) return self.diff(other, False) def __add__(self, other: datetime.timedelta) -> Self: if not isinstance(other, datetime.timedelta): return NotImplemented caller = traceback.extract_stack(limit=2)[0].name if caller == "astimezone": return super().__add__(other) return self._add_timedelta_(other) def __radd__(self, other: datetime.timedelta) -> Self: return self.__add__(other) # Native methods override @classmethod def fromtimestamp(cls, t: float, tz: datetime.tzinfo | None = None) -> Self: tzinfo = pendulum._safe_timezone(tz) return cls.instance(datetime.datetime.fromtimestamp(t, tz=tzinfo), tz=tzinfo) @classmethod def utcfromtimestamp(cls, t: float) -> Self: return cls.instance(datetime.datetime.utcfromtimestamp(t), tz=None) @classmethod def fromordinal(cls, n: int) -> Self: return cls.instance(datetime.datetime.fromordinal(n), tz=None) @classmethod def combine( cls, date: datetime.date, time: datetime.time, tzinfo: datetime.tzinfo | None = None, ) -> Self: return cls.instance(datetime.datetime.combine(date, time), tz=tzinfo) def astimezone(self, tz: datetime.tzinfo | None = None) -> Self: dt = super().astimezone(tz) return self.__class__( dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, dt.microsecond, fold=dt.fold, tzinfo=dt.tzinfo, ) def replace( self, year: SupportsIndex | None = None, month: SupportsIndex | None = None, day: SupportsIndex | None = None, hour: SupportsIndex | None = None, minute: SupportsIndex | None = None, second: SupportsIndex | None = None, microsecond: SupportsIndex | None = None, tzinfo: bool | datetime.tzinfo | Literal[True] | None = True, fold: int | None = None, ) -> Self: if year is None: year = self.year if month is None: month = self.month if day is None: day = self.day if hour is None: hour = self.hour if minute is None: minute = self.minute if second is None: second = self.second if microsecond is None: microsecond = self.microsecond if tzinfo is True: tzinfo = self.tzinfo if fold is None: fold = self.fold if tzinfo is not None: tzinfo = pendulum._safe_timezone(tzinfo) return self.__class__.create( year, month, day, hour, minute, second, microsecond, tz=tzinfo, fold=fold, ) def __getnewargs__(self) -> tuple[Self]: return (self,) def _getstate( self, protocol: SupportsIndex = 3 ) -> tuple[int, int, int, int, int, int, int, datetime.tzinfo | None]: return ( self.year, self.month, self.day, self.hour, self.minute, self.second, self.microsecond, self.tzinfo, ) def __reduce__( self, ) -> tuple[ type[Self], tuple[int, int, int, int, int, int, int, datetime.tzinfo | None], ]: return self.__reduce_ex__(2) def __reduce_ex__( self, protocol: SupportsIndex ) -> tuple[ type[Self], tuple[int, int, int, int, int, int, int, datetime.tzinfo | None], ]: return self.__class__, self._getstate(protocol) def __deepcopy__(self, _: dict[int, Self]) -> Self: return self.__class__( self.year, self.month, self.day, self.hour, self.minute, self.second, self.microsecond, tzinfo=self.tz, fold=self.fold, ) def _cmp(self, other: datetime.datetime, **kwargs: Any) -> int: # Fix for pypy which compares using this method # which would lead to infinite recursion if we didn't override dt = datetime.datetime( self.year, self.month, self.day, self.hour, self.minute, self.second, self.microsecond, tzinfo=self.tz, fold=self.fold, ) return 0 if dt == other else 1 if dt > other else -1 DateTime.min = DateTime(1, 1, 1, 0, 0, tzinfo=UTC) DateTime.max = DateTime(9999, 12, 31, 23, 59, 59, 999999, tzinfo=UTC) DateTime.EPOCH = DateTime(1970, 1, 1, tzinfo=UTC) pendulum-3.0.0/src/pendulum/day.py000066400000000000000000000003101453741033500171110ustar00rootroot00000000000000from __future__ import annotations from enum import IntEnum class WeekDay(IntEnum): MONDAY = 0 TUESDAY = 1 WEDNESDAY = 2 THURSDAY = 3 FRIDAY = 4 SATURDAY = 5 SUNDAY = 6 pendulum-3.0.0/src/pendulum/duration.py000066400000000000000000000346741453741033500202050ustar00rootroot00000000000000from __future__ import annotations from datetime import timedelta from typing import TYPE_CHECKING from typing import cast from typing import overload import pendulum from pendulum.constants import SECONDS_PER_DAY from pendulum.constants import SECONDS_PER_HOUR from pendulum.constants import SECONDS_PER_MINUTE from pendulum.constants import US_PER_SECOND from pendulum.utils._compat import PYPY if TYPE_CHECKING: from typing_extensions import Self def _divide_and_round(a: float, b: float) -> int: """divide a by b and round result to the nearest integer When the ratio is exactly half-way between two integers, the even integer is returned. """ # Based on the reference implementation for divmod_near # in Objects/longobject.c. q, r = divmod(a, b) # The output of divmod() is either a float or an int, # but we always want it to be an int. q = int(q) # round up if either r / b > 0.5, or r / b == 0.5 and q is odd. # The expression r / b > 0.5 is equivalent to 2 * r > b if b is # positive, 2 * r < b if b negative. r *= 2 greater_than_half = r > b if b > 0 else r < b if greater_than_half or r == b and q % 2 == 1: q += 1 return q class Duration(timedelta): """ Replacement for the standard timedelta class. Provides several improvements over the base class. """ _total: float = 0 _years: int = 0 _months: int = 0 _weeks: int = 0 _days: int = 0 _remaining_days: int = 0 _seconds: int = 0 _microseconds: int = 0 _y = None _m = None _w = None _d = None _h = None _i = None _s = None _invert = None def __new__( cls, days: float = 0, seconds: float = 0, microseconds: float = 0, milliseconds: float = 0, minutes: float = 0, hours: float = 0, weeks: float = 0, years: float = 0, months: float = 0, ) -> Self: if not isinstance(years, int) or not isinstance(months, int): raise ValueError("Float year and months are not supported") self = timedelta.__new__( cls, days + years * 365 + months * 30, seconds, microseconds, milliseconds, minutes, hours, weeks, ) # Intuitive normalization total = self.total_seconds() - (years * 365 + months * 30) * SECONDS_PER_DAY self._total = total m = 1 if total < 0: m = -1 self._microseconds = round(total % m * 1e6) self._seconds = abs(int(total)) % SECONDS_PER_DAY * m _days = abs(int(total)) // SECONDS_PER_DAY * m self._days = _days self._remaining_days = abs(_days) % 7 * m self._weeks = abs(_days) // 7 * m self._months = months self._years = years self._signature = { # type: ignore[attr-defined] "years": years, "months": months, "weeks": weeks, "days": days, "hours": hours, "minutes": minutes, "seconds": seconds, "microseconds": microseconds + milliseconds * 1000, } return self def total_minutes(self) -> float: return self.total_seconds() / SECONDS_PER_MINUTE def total_hours(self) -> float: return self.total_seconds() / SECONDS_PER_HOUR def total_days(self) -> float: return self.total_seconds() / SECONDS_PER_DAY def total_weeks(self) -> float: return self.total_days() / 7 if PYPY: def total_seconds(self) -> float: days = 0 if hasattr(self, "_years"): days += self._years * 365 if hasattr(self, "_months"): days += self._months * 30 if hasattr(self, "_remaining_days"): days += self._weeks * 7 + self._remaining_days else: days += self._days return ( (days * SECONDS_PER_DAY + self._seconds) * US_PER_SECOND + self._microseconds ) / US_PER_SECOND @property def years(self) -> int: return self._years @property def months(self) -> int: return self._months @property def weeks(self) -> int: return self._weeks if PYPY: @property def days(self) -> int: return self._years * 365 + self._months * 30 + self._days @property def remaining_days(self) -> int: return self._remaining_days @property def hours(self) -> int: if self._h is None: seconds = self._seconds self._h = 0 if abs(seconds) >= 3600: self._h = (abs(seconds) // 3600 % 24) * self._sign(seconds) return self._h @property def minutes(self) -> int: if self._i is None: seconds = self._seconds self._i = 0 if abs(seconds) >= 60: self._i = (abs(seconds) // 60 % 60) * self._sign(seconds) return self._i @property def seconds(self) -> int: return self._seconds @property def remaining_seconds(self) -> int: if self._s is None: self._s = self._seconds self._s = abs(self._s) % 60 * self._sign(self._s) return self._s @property def microseconds(self) -> int: return self._microseconds @property def invert(self) -> bool: if self._invert is None: self._invert = self.total_seconds() < 0 return self._invert def in_weeks(self) -> int: return int(self.total_weeks()) def in_days(self) -> int: return int(self.total_days()) def in_hours(self) -> int: return int(self.total_hours()) def in_minutes(self) -> int: return int(self.total_minutes()) def in_seconds(self) -> int: return int(self.total_seconds()) def in_words(self, locale: str | None = None, separator: str = " ") -> str: """ Get the current interval in words in the current locale. Ex: 6 jours 23 heures 58 minutes :param locale: The locale to use. Defaults to current locale. :param separator: The separator to use between each unit """ intervals = [ ("year", self.years), ("month", self.months), ("week", self.weeks), ("day", self.remaining_days), ("hour", self.hours), ("minute", self.minutes), ("second", self.remaining_seconds), ] if locale is None: locale = pendulum.get_locale() loaded_locale = pendulum.locale(locale) parts = [] for interval in intervals: unit, interval_count = interval if abs(interval_count) > 0: translation = loaded_locale.translation( f"units.{unit}.{loaded_locale.plural(abs(interval_count))}" ) parts.append(translation.format(interval_count)) if not parts: count: int | str = 0 if abs(self.microseconds) > 0: unit = f"units.second.{loaded_locale.plural(1)}" count = f"{abs(self.microseconds) / 1e6:.2f}" else: unit = f"units.microsecond.{loaded_locale.plural(0)}" translation = loaded_locale.translation(unit) parts.append(translation.format(count)) return separator.join(parts) def _sign(self, value: float) -> int: if value < 0: return -1 return 1 def as_timedelta(self) -> timedelta: """ Return the interval as a native timedelta. """ return timedelta(seconds=self.total_seconds()) def __str__(self) -> str: return self.in_words() def __repr__(self) -> str: rep = f"{self.__class__.__name__}(" if self._years: rep += f"years={self._years}, " if self._months: rep += f"months={self._months}, " if self._weeks: rep += f"weeks={self._weeks}, " if self._days: rep += f"days={self._remaining_days}, " if self.hours: rep += f"hours={self.hours}, " if self.minutes: rep += f"minutes={self.minutes}, " if self.remaining_seconds: rep += f"seconds={self.remaining_seconds}, " if self.microseconds: rep += f"microseconds={self.microseconds}, " rep += ")" return rep.replace(", )", ")") def __add__(self, other: timedelta) -> Self: if isinstance(other, timedelta): return self.__class__(seconds=self.total_seconds() + other.total_seconds()) return NotImplemented __radd__ = __add__ def __sub__(self, other: timedelta) -> Self: if isinstance(other, timedelta): return self.__class__(seconds=self.total_seconds() - other.total_seconds()) return NotImplemented def __neg__(self) -> Self: return self.__class__( years=-self._years, months=-self._months, weeks=-self._weeks, days=-self._remaining_days, seconds=-self._seconds, microseconds=-self._microseconds, ) def _to_microseconds(self) -> int: return (self._days * (24 * 3600) + self._seconds) * 1000000 + self._microseconds def __mul__(self, other: int | float) -> Self: if isinstance(other, int): return self.__class__( years=self._years * other, months=self._months * other, seconds=self._total * other, ) if isinstance(other, float): usec = self._to_microseconds() a, b = other.as_integer_ratio() return self.__class__(0, 0, _divide_and_round(usec * a, b)) return NotImplemented __rmul__ = __mul__ @overload def __floordiv__(self, other: timedelta) -> int: ... @overload def __floordiv__(self, other: int) -> Self: ... def __floordiv__(self, other: int | timedelta) -> int | Duration: if not isinstance(other, (int, timedelta)): return NotImplemented usec = self._to_microseconds() if isinstance(other, timedelta): return cast( int, usec // other._to_microseconds() # type: ignore[attr-defined] ) if isinstance(other, int): return self.__class__( 0, 0, usec // other, years=self._years // other, months=self._months // other, ) @overload def __truediv__(self, other: timedelta) -> float: ... @overload def __truediv__(self, other: float) -> Self: ... def __truediv__(self, other: int | float | timedelta) -> Self | float: if not isinstance(other, (int, float, timedelta)): return NotImplemented usec = self._to_microseconds() if isinstance(other, timedelta): return cast( float, usec / other._to_microseconds() # type: ignore[attr-defined] ) if isinstance(other, int): return self.__class__( 0, 0, _divide_and_round(usec, other), years=_divide_and_round(self._years, other), months=_divide_and_round(self._months, other), ) if isinstance(other, float): a, b = other.as_integer_ratio() return self.__class__( 0, 0, _divide_and_round(b * usec, a), years=_divide_and_round(self._years * b, a), months=_divide_and_round(self._months, other), ) __div__ = __floordiv__ def __mod__(self, other: timedelta) -> Self: if isinstance(other, timedelta): r = self._to_microseconds() % other._to_microseconds() # type: ignore[attr-defined] # noqa: E501 return self.__class__(0, 0, r) return NotImplemented def __divmod__(self, other: timedelta) -> tuple[int, Duration]: if isinstance(other, timedelta): q, r = divmod( self._to_microseconds(), other._to_microseconds(), # type: ignore[attr-defined] ) return q, self.__class__(0, 0, r) return NotImplemented def __deepcopy__(self, _: dict[int, Self]) -> Self: return self.__class__( days=self.remaining_days, seconds=self.remaining_seconds, microseconds=self.microseconds, minutes=self.minutes, hours=self.hours, years=self.years, months=self.months, ) Duration.min = Duration(days=-999999999) Duration.max = Duration( days=999999999, hours=23, minutes=59, seconds=59, microseconds=999999 ) Duration.resolution = Duration(microseconds=1) class AbsoluteDuration(Duration): """ Duration that expresses a time difference in absolute values. """ def __new__( cls, days: float = 0, seconds: float = 0, microseconds: float = 0, milliseconds: float = 0, minutes: float = 0, hours: float = 0, weeks: float = 0, years: float = 0, months: float = 0, ) -> AbsoluteDuration: if not isinstance(years, int) or not isinstance(months, int): raise ValueError("Float year and months are not supported") self = timedelta.__new__( cls, days, seconds, microseconds, milliseconds, minutes, hours, weeks ) # We need to compute the total_seconds() value # on a native timedelta object delta = timedelta( days, seconds, microseconds, milliseconds, minutes, hours, weeks ) # Intuitive normalization self._total = delta.total_seconds() total = abs(self._total) self._microseconds = round(total % 1 * 1e6) days, self._seconds = divmod(int(total), SECONDS_PER_DAY) self._days = abs(days + years * 365 + months * 30) self._weeks, self._remaining_days = divmod(days, 7) self._months = abs(months) self._years = abs(years) return self def total_seconds(self) -> float: return abs(self._total) @property def invert(self) -> bool: if self._invert is None: self._invert = self._total < 0 return self._invert pendulum-3.0.0/src/pendulum/exceptions.py000066400000000000000000000003031453741033500205170ustar00rootroot00000000000000from __future__ import annotations from pendulum.parsing.exceptions import ParserError class PendulumException(Exception): pass __all__ = [ "ParserError", "PendulumException", ] pendulum-3.0.0/src/pendulum/formatting/000077500000000000000000000000001453741033500201425ustar00rootroot00000000000000pendulum-3.0.0/src/pendulum/formatting/__init__.py000066400000000000000000000001621453741033500222520ustar00rootroot00000000000000from __future__ import annotations from pendulum.formatting.formatter import Formatter __all__ = ["Formatter"] pendulum-3.0.0/src/pendulum/formatting/difference_formatter.py000066400000000000000000000101421453741033500246670ustar00rootroot00000000000000from __future__ import annotations import typing as t from pendulum.locales.locale import Locale if t.TYPE_CHECKING: from pendulum import Duration class DifferenceFormatter: """ Handles formatting differences in text. """ def __init__(self, locale: str = "en") -> None: self._locale = Locale.load(locale) def format( self, diff: Duration, is_now: bool = True, absolute: bool = False, locale: str | Locale | None = None, ) -> str: """ Formats a difference. :param diff: The difference to format :param is_now: Whether the difference includes now :param absolute: Whether it's an absolute difference or not :param locale: The locale to use """ locale = self._locale if locale is None else Locale.load(locale) if diff.years > 0: unit = "year" count = diff.years if diff.months > 6: count += 1 elif diff.months == 11 and (diff.weeks * 7 + diff.remaining_days) > 15: unit = "year" count = 1 elif diff.months > 0: unit = "month" count = diff.months if (diff.weeks * 7 + diff.remaining_days) >= 27: count += 1 elif diff.weeks > 0: unit = "week" count = diff.weeks if diff.remaining_days > 3: count += 1 elif diff.remaining_days > 0: unit = "day" count = diff.remaining_days if diff.hours >= 22: count += 1 elif diff.hours > 0: unit = "hour" count = diff.hours elif diff.minutes > 0: unit = "minute" count = diff.minutes elif 10 < diff.remaining_seconds <= 59: unit = "second" count = diff.remaining_seconds else: # We check if the "a few seconds" unit exists time = locale.get("custom.units.few_second") if time is not None: if absolute: return t.cast(str, time) key = "custom" is_future = diff.invert if is_now: if is_future: key += ".from_now" else: key += ".ago" else: if is_future: key += ".after" else: key += ".before" return t.cast(str, locale.get(key).format(time)) else: unit = "second" count = diff.remaining_seconds if count == 0: count = 1 if absolute: key = f"translations.units.{unit}" else: is_future = diff.invert if is_now: # Relative to now, so we can use # the CLDR data key = f"translations.relative.{unit}" if is_future: key += ".future" else: key += ".past" else: # Absolute comparison # So we have to use the custom locale data # Checking for special pluralization rules key = "custom.units_relative" if is_future: key += f".{unit}.future" else: key += f".{unit}.past" trans = locale.get(key) if not trans: # No special rule key = f"translations.units.{unit}.{locale.plural(count)}" time = locale.get(key).format(count) else: time = trans[locale.plural(count)].format(count) key = "custom" if is_future: key += ".after" else: key += ".before" return t.cast(str, locale.get(key).format(time)) key += f".{locale.plural(count)}" return t.cast(str, locale.get(key).format(count)) pendulum-3.0.0/src/pendulum/formatting/formatter.py000066400000000000000000000537431453741033500225330ustar00rootroot00000000000000from __future__ import annotations import datetime import re from typing import TYPE_CHECKING from typing import Any from typing import Callable from typing import ClassVar from typing import Match from typing import Sequence from typing import cast import pendulum from pendulum.locales.locale import Locale if TYPE_CHECKING: from pendulum import Timezone _MATCH_1 = r"\d" _MATCH_2 = r"\d\d" _MATCH_3 = r"\d{3}" _MATCH_4 = r"\d{4}" _MATCH_6 = r"[+-]?\d{6}" _MATCH_1_TO_2 = r"\d\d?" _MATCH_1_TO_2_LEFT_PAD = r"[0-9 ]\d?" _MATCH_1_TO_3 = r"\d{1,3}" _MATCH_1_TO_4 = r"\d{1,4}" _MATCH_1_TO_6 = r"[+-]?\d{1,6}" _MATCH_3_TO_4 = r"\d{3}\d?" _MATCH_5_TO_6 = r"\d{5}\d?" _MATCH_UNSIGNED = r"\d+" _MATCH_SIGNED = r"[+-]?\d+" _MATCH_OFFSET = r"[Zz]|[+-]\d\d:?\d\d" _MATCH_SHORT_OFFSET = r"[Zz]|[+-]\d\d(?::?\d\d)?" _MATCH_TIMESTAMP = r"[+-]?\d+(\.\d{1,6})?" _MATCH_WORD = ( "(?i)[0-9]*" "['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+" r"|[\u0600-\u06FF/]+(\s*?[\u0600-\u06FF]+){1,2}" ) _MATCH_TIMEZONE = "[A-Za-z0-9-+]+(/[A-Za-z0-9-+_]+)?" class Formatter: _TOKENS: str = ( r"\[([^\[]*)\]|\\(.)|" "(" "Mo|MM?M?M?" "|Do|DDDo|DD?D?D?|ddd?d?|do?|eo?" "|E{1,4}" "|w[o|w]?|W[o|W]?|Qo?" "|YYYY|YY|Y" "|gg(ggg?)?|GG(GGG?)?" "|a|A" "|hh?|HH?|kk?" "|mm?|ss?|S{1,9}" "|x|X" "|zz?|ZZ?" "|LTS|LT|LL?L?L?" ")" ) _FORMAT_RE: re.Pattern[str] = re.compile(_TOKENS) _FROM_FORMAT_RE: re.Pattern[str] = re.compile(r"(? str: """ Formats a DateTime instance with a given format and locale. :param dt: The instance to format :param fmt: The format to use :param locale: The locale to use """ loaded_locale: Locale = Locale.load(locale or pendulum.get_locale()) result = self._FORMAT_RE.sub( lambda m: m.group(1) if m.group(1) else m.group(2) if m.group(2) else self._format_token(dt, m.group(3), loaded_locale), fmt, ) return result def _format_token(self, dt: pendulum.DateTime, token: str, locale: Locale) -> str: """ Formats a DateTime instance with a given token and locale. :param dt: The instance to format :param token: The token to use :param locale: The locale to use """ if token in self._DATE_FORMATS: fmt = locale.get(f"custom.date_formats.{token}") if fmt is None: fmt = self._DEFAULT_DATE_FORMATS[token] return self.format(dt, fmt, locale) if token in self._LOCALIZABLE_TOKENS: return self._format_localizable_token(dt, token, locale) if token in self._TOKENS_RULES: return self._TOKENS_RULES[token](dt) # Timezone if token in ["ZZ", "Z"]: if dt.tzinfo is None: return "" separator = ":" if token == "Z" else "" offset = dt.utcoffset() or datetime.timedelta() minutes = offset.total_seconds() / 60 sign = "+" if minutes >= 0 else "-" hour, minute = divmod(abs(int(minutes)), 60) return f"{sign}{hour:02d}{separator}{minute:02d}" return token def _format_localizable_token( self, dt: pendulum.DateTime, token: str, locale: Locale ) -> str: """ Formats a DateTime instance with a given localizable token and locale. :param dt: The instance to format :param token: The token to use :param locale: The locale to use """ if token == "MMM": return cast(str, locale.get("translations.months.abbreviated")[dt.month]) elif token == "MMMM": return cast(str, locale.get("translations.months.wide")[dt.month]) elif token == "dd": return cast(str, locale.get("translations.days.short")[dt.day_of_week]) elif token == "ddd": return cast( str, locale.get("translations.days.abbreviated")[dt.day_of_week], ) elif token == "dddd": return cast(str, locale.get("translations.days.wide")[dt.day_of_week]) elif token == "e": first_day = cast(int, locale.get("translations.week_data.first_day")) return str((dt.day_of_week % 7 - first_day) % 7) elif token == "Do": return locale.ordinalize(dt.day) elif token == "do": return locale.ordinalize((dt.day_of_week + 1) % 7) elif token == "Mo": return locale.ordinalize(dt.month) elif token == "Qo": return locale.ordinalize(dt.quarter) elif token == "wo": return locale.ordinalize(dt.week_of_year) elif token == "DDDo": return locale.ordinalize(dt.day_of_year) elif token == "eo": first_day = cast(int, locale.get("translations.week_data.first_day")) return locale.ordinalize((dt.day_of_week % 7 - first_day) % 7 + 1) elif token == "A": key = "translations.day_periods" if dt.hour >= 12: key += ".pm" else: key += ".am" return cast(str, locale.get(key)) else: return token def parse( self, time: str, fmt: str, now: pendulum.DateTime, locale: str | None = None, ) -> dict[str, Any]: """ Parses a time string matching a given format as a tuple. :param time: The timestring :param fmt: The format :param now: The datetime to use as "now" :param locale: The locale to use :return: The parsed elements """ escaped_fmt = re.escape(fmt) tokens = self._FROM_FORMAT_RE.findall(escaped_fmt) if not tokens: raise ValueError("The given time string does not match the given format") if not locale: locale = pendulum.get_locale() loaded_locale: Locale = Locale.load(locale) parsed = { "year": None, "month": None, "day": None, "hour": None, "minute": None, "second": None, "microsecond": None, "tz": None, "quarter": None, "day_of_week": None, "day_of_year": None, "meridiem": None, "timestamp": None, } pattern = self._FROM_FORMAT_RE.sub( lambda m: self._replace_tokens(m.group(0), loaded_locale), escaped_fmt ) if not re.search("^" + pattern + "$", time): raise ValueError(f"String does not match format {fmt}") def _get_parsed_values(m: Match[str]) -> Any: return self._get_parsed_values(m, parsed, loaded_locale, now) re.sub(pattern, _get_parsed_values, time) return self._check_parsed(parsed, now) def _check_parsed( self, parsed: dict[str, Any], now: pendulum.DateTime ) -> dict[str, Any]: """ Checks validity of parsed elements. :param parsed: The elements to parse. :return: The validated elements. """ validated: dict[str, int | Timezone | None] = { "year": parsed["year"], "month": parsed["month"], "day": parsed["day"], "hour": parsed["hour"], "minute": parsed["minute"], "second": parsed["second"], "microsecond": parsed["microsecond"], "tz": None, } # If timestamp has been specified # we use it and don't go any further if parsed["timestamp"] is not None: str_us = str(parsed["timestamp"]) if "." in str_us: microseconds = int(f'{str_us.split(".")[1].ljust(6, "0")}') else: microseconds = 0 from pendulum.helpers import local_time time = local_time(parsed["timestamp"], 0, microseconds) validated["year"] = time[0] validated["month"] = time[1] validated["day"] = time[2] validated["hour"] = time[3] validated["minute"] = time[4] validated["second"] = time[5] validated["microsecond"] = time[6] return validated if parsed["quarter"] is not None: if validated["year"] is not None: dt = pendulum.datetime(cast(int, validated["year"]), 1, 1) else: dt = now dt = dt.start_of("year") while dt.quarter != parsed["quarter"]: dt = dt.add(months=3) validated["year"] = dt.year validated["month"] = dt.month validated["day"] = dt.day if validated["year"] is None: validated["year"] = now.year if parsed["day_of_year"] is not None: dt = cast( pendulum.DateTime, pendulum.parse(f'{validated["year"]}-{parsed["day_of_year"]:>03d}'), ) validated["month"] = dt.month validated["day"] = dt.day if parsed["day_of_week"] is not None: dt = pendulum.datetime( cast(int, validated["year"]), cast(int, validated["month"]) or now.month, cast(int, validated["day"]) or now.day, ) dt = dt.start_of("week").subtract(days=1) dt = dt.next(parsed["day_of_week"]) validated["year"] = dt.year validated["month"] = dt.month validated["day"] = dt.day # Meridiem if parsed["meridiem"] is not None: # If the time is greater than 13:00:00 # This is not valid if validated["hour"] is None: raise ValueError("Invalid Date") t = ( validated["hour"], validated["minute"], validated["second"], validated["microsecond"], ) if t >= (13, 0, 0, 0): raise ValueError("Invalid date") pm = parsed["meridiem"] == "pm" validated["hour"] %= 12 # type: ignore[operator] if pm: validated["hour"] += 12 # type: ignore[operator] if validated["month"] is None: if parsed["year"] is not None: validated["month"] = parsed["month"] or 1 else: validated["month"] = parsed["month"] or now.month if validated["day"] is None: if parsed["year"] is not None or parsed["month"] is not None: validated["day"] = parsed["day"] or 1 else: validated["day"] = parsed["day"] or now.day for part in ["hour", "minute", "second", "microsecond"]: if validated[part] is None: validated[part] = 0 validated["tz"] = parsed["tz"] return validated def _get_parsed_values( self, m: Match[str], parsed: dict[str, Any], locale: Locale, now: pendulum.DateTime, ) -> None: for token, index in m.re.groupindex.items(): if token in self._LOCALIZABLE_TOKENS: self._get_parsed_locale_value(token, m.group(index), parsed, locale) else: self._get_parsed_value(token, m.group(index), parsed, now) def _get_parsed_value( self, token: str, value: str, parsed: dict[str, Any], now: pendulum.DateTime, ) -> None: parsed_token = self._PARSE_TOKENS[token](value) if "Y" in token: if token == "YY": if parsed_token <= 68: parsed_token += 2000 else: parsed_token += 1900 parsed["year"] = parsed_token elif token == "Q": parsed["quarter"] = parsed_token elif token in ["MM", "M"]: parsed["month"] = parsed_token elif token in ["DDDD", "DDD"]: parsed["day_of_year"] = parsed_token elif "D" in token: parsed["day"] = parsed_token elif "H" in token: parsed["hour"] = parsed_token elif token in ["hh", "h"]: if parsed_token > 12: raise ValueError("Invalid date") parsed["hour"] = parsed_token elif "m" in token: parsed["minute"] = parsed_token elif "s" in token: parsed["second"] = parsed_token elif "S" in token: parsed["microsecond"] = parsed_token elif token in ["d", "E"]: parsed["day_of_week"] = parsed_token elif token in ["X", "x"]: parsed["timestamp"] = parsed_token elif token in ["ZZ", "Z"]: negative = bool(value.startswith("-")) tz = value[1:] if ":" not in tz: if len(tz) == 2: tz = f"{tz}00" off_hour = tz[0:2] off_minute = tz[2:4] else: off_hour, off_minute = tz.split(":") offset = ((int(off_hour) * 60) + int(off_minute)) * 60 if negative: offset = -1 * offset parsed["tz"] = pendulum.timezone(offset) elif token == "z": # Full timezone if value not in pendulum.timezones(): raise ValueError("Invalid date") parsed["tz"] = pendulum.timezone(value) def _get_parsed_locale_value( self, token: str, value: str, parsed: dict[str, Any], locale: Locale ) -> None: if token == "MMMM": unit = "month" match = "months.wide" elif token == "MMM": unit = "month" match = "months.abbreviated" elif token == "Do": parsed["day"] = int(cast(Match[str], re.match(r"(\d+)", value)).group(1)) return elif token == "dddd": unit = "day_of_week" match = "days.wide" elif token == "ddd": unit = "day_of_week" match = "days.abbreviated" elif token == "dd": unit = "day_of_week" match = "days.short" elif token in ["a", "A"]: valid_values = [ locale.translation("day_periods.am"), locale.translation("day_periods.pm"), ] if token == "a": value = value.lower() valid_values = [x.lower() for x in valid_values] if value not in valid_values: raise ValueError("Invalid date") parsed["meridiem"] = ["am", "pm"][valid_values.index(value)] return else: raise ValueError(f'Invalid token "{token}"') parsed[unit] = locale.match_translation(match, value) if value is None: raise ValueError("Invalid date") def _replace_tokens(self, token: str, locale: Locale) -> str: if token.startswith("[") and token.endswith("]"): return token[1:-1] elif token.startswith("\\"): if len(token) == 2 and token[1] in {"[", "]"}: return "" return token elif token not in self._REGEX_TOKENS and token not in self._LOCALIZABLE_TOKENS: raise ValueError(f"Unsupported token: {token}") if token in self._LOCALIZABLE_TOKENS: values = self._LOCALIZABLE_TOKENS[token] if callable(values): candidates = values(locale) else: candidates = tuple( locale.translation( cast(str, self._LOCALIZABLE_TOKENS[token]) ).values() ) else: candidates = cast(Sequence[str], self._REGEX_TOKENS[token]) if not candidates: raise ValueError(f"Unsupported token: {token}") if not isinstance(candidates, tuple): candidates = (cast(str, candidates),) pattern = f'(?P<{token}>{"|".join(candidates)})' return pattern pendulum-3.0.0/src/pendulum/helpers.py000066400000000000000000000115571453741033500200150ustar00rootroot00000000000000from __future__ import annotations import os import struct from datetime import date from datetime import datetime from datetime import timedelta from math import copysign from typing import TYPE_CHECKING from typing import TypeVar from typing import overload import pendulum from pendulum.constants import DAYS_PER_MONTHS from pendulum.day import WeekDay from pendulum.formatting.difference_formatter import DifferenceFormatter from pendulum.locales.locale import Locale if TYPE_CHECKING: # Prevent import cycles from pendulum.duration import Duration with_extensions = os.getenv("PENDULUM_EXTENSIONS", "1") == "1" _DT = TypeVar("_DT", bound=datetime) _D = TypeVar("_D", bound=date) try: if not with_extensions or struct.calcsize("P") == 4: raise ImportError() from pendulum._pendulum import PreciseDiff from pendulum._pendulum import days_in_year from pendulum._pendulum import is_leap from pendulum._pendulum import is_long_year from pendulum._pendulum import local_time from pendulum._pendulum import precise_diff from pendulum._pendulum import week_day except ImportError: from pendulum._helpers import PreciseDiff # type: ignore[assignment] from pendulum._helpers import days_in_year from pendulum._helpers import is_leap from pendulum._helpers import is_long_year from pendulum._helpers import local_time from pendulum._helpers import precise_diff # type: ignore[assignment] from pendulum._helpers import week_day difference_formatter = DifferenceFormatter() @overload def add_duration( dt: _DT, years: int = 0, months: int = 0, weeks: int = 0, days: int = 0, hours: int = 0, minutes: int = 0, seconds: float = 0, microseconds: int = 0, ) -> _DT: ... @overload def add_duration( dt: _D, years: int = 0, months: int = 0, weeks: int = 0, days: int = 0, ) -> _D: pass def add_duration( dt: date | datetime, years: int = 0, months: int = 0, weeks: int = 0, days: int = 0, hours: int = 0, minutes: int = 0, seconds: float = 0, microseconds: int = 0, ) -> date | datetime: """ Adds a duration to a date/datetime instance. """ days += weeks * 7 if ( isinstance(dt, date) and not isinstance(dt, datetime) and any([hours, minutes, seconds, microseconds]) ): raise RuntimeError("Time elements cannot be added to a date instance.") # Normalizing if abs(microseconds) > 999999: s = _sign(microseconds) div, mod = divmod(microseconds * s, 1000000) microseconds = mod * s seconds += div * s if abs(seconds) > 59: s = _sign(seconds) div, mod = divmod(seconds * s, 60) # type: ignore[assignment] seconds = mod * s minutes += div * s if abs(minutes) > 59: s = _sign(minutes) div, mod = divmod(minutes * s, 60) minutes = mod * s hours += div * s if abs(hours) > 23: s = _sign(hours) div, mod = divmod(hours * s, 24) hours = mod * s days += div * s if abs(months) > 11: s = _sign(months) div, mod = divmod(months * s, 12) months = mod * s years += div * s year = dt.year + years month = dt.month if months: month += months if month > 12: year += 1 month -= 12 elif month < 1: year -= 1 month += 12 day = min(DAYS_PER_MONTHS[int(is_leap(year))][month], dt.day) dt = dt.replace(year=year, month=month, day=day) return dt + timedelta( days=days, hours=hours, minutes=minutes, seconds=seconds, microseconds=microseconds, ) def format_diff( diff: Duration, is_now: bool = True, absolute: bool = False, locale: str | None = None, ) -> str: if locale is None: locale = get_locale() return difference_formatter.format(diff, is_now, absolute, locale) def _sign(x: float) -> int: return int(copysign(1, x)) # Global helpers def locale(name: str) -> Locale: return Locale.load(name) def set_locale(name: str) -> None: locale(name) pendulum._LOCALE = name def get_locale() -> str: return pendulum._LOCALE def week_starts_at(wday: WeekDay) -> None: if wday < WeekDay.MONDAY or wday > WeekDay.SUNDAY: raise ValueError("Invalid day of week") pendulum._WEEK_STARTS_AT = wday def week_ends_at(wday: WeekDay) -> None: if wday < WeekDay.MONDAY or wday > WeekDay.SUNDAY: raise ValueError("Invalid day of week") pendulum._WEEK_ENDS_AT = wday __all__ = [ "PreciseDiff", "days_in_year", "is_leap", "is_long_year", "local_time", "precise_diff", "week_day", "add_duration", "format_diff", "locale", "set_locale", "get_locale", "week_starts_at", "week_ends_at", ] pendulum-3.0.0/src/pendulum/interval.py000066400000000000000000000323471453741033500201770ustar00rootroot00000000000000from __future__ import annotations import operator from datetime import date from datetime import datetime from datetime import timedelta from typing import TYPE_CHECKING from typing import Iterator from typing import Union from typing import cast from typing import overload import pendulum from pendulum.constants import MONTHS_PER_YEAR from pendulum.duration import Duration from pendulum.helpers import precise_diff if TYPE_CHECKING: from typing_extensions import Self from typing_extensions import SupportsIndex from pendulum.helpers import PreciseDiff from pendulum.locales.locale import Locale class Interval(Duration): """ An interval of time between two datetimes. """ @overload def __new__( cls, start: pendulum.DateTime | datetime, end: pendulum.DateTime | datetime, absolute: bool = False, ) -> Self: ... @overload def __new__( cls, start: pendulum.Date | date, end: pendulum.Date | date, absolute: bool = False, ) -> Self: ... def __new__( cls, start: pendulum.DateTime | pendulum.Date | datetime | date, end: pendulum.DateTime | pendulum.Date | datetime | date, absolute: bool = False, ) -> Self: if ( isinstance(start, datetime) and not isinstance(end, datetime) or not isinstance(start, datetime) and isinstance(end, datetime) ): raise ValueError( "Both start and end of an Interval must have the same type" ) if ( isinstance(start, datetime) and isinstance(end, datetime) and ( start.tzinfo is None and end.tzinfo is not None or start.tzinfo is not None and end.tzinfo is None ) ): raise TypeError("can't compare offset-naive and offset-aware datetimes") if absolute and start > end: end, start = start, end _start = start _end = end if isinstance(start, pendulum.DateTime): _start = datetime( start.year, start.month, start.day, start.hour, start.minute, start.second, start.microsecond, tzinfo=start.tzinfo, fold=start.fold, ) elif isinstance(start, pendulum.Date): _start = date(start.year, start.month, start.day) if isinstance(end, pendulum.DateTime): _end = datetime( end.year, end.month, end.day, end.hour, end.minute, end.second, end.microsecond, tzinfo=end.tzinfo, fold=end.fold, ) elif isinstance(end, pendulum.Date): _end = date(end.year, end.month, end.day) # Fixing issues with datetime.__sub__() # not handling offsets if the tzinfo is the same if ( isinstance(_start, datetime) and isinstance(_end, datetime) and _start.tzinfo is _end.tzinfo ): if _start.tzinfo is not None: offset = cast(timedelta, cast(datetime, start).utcoffset()) _start = (_start - offset).replace(tzinfo=None) if isinstance(end, datetime) and _end.tzinfo is not None: offset = cast(timedelta, end.utcoffset()) _end = (_end - offset).replace(tzinfo=None) delta: timedelta = _end - _start # type: ignore[operator] return super().__new__(cls, seconds=delta.total_seconds()) def __init__( self, start: pendulum.DateTime | pendulum.Date | datetime | date, end: pendulum.DateTime | pendulum.Date | datetime | date, absolute: bool = False, ) -> None: super().__init__() _start: pendulum.DateTime | pendulum.Date | datetime | date if not isinstance(start, pendulum.Date): if isinstance(start, datetime): start = pendulum.instance(start) else: start = pendulum.date(start.year, start.month, start.day) _start = start else: if isinstance(start, pendulum.DateTime): _start = datetime( start.year, start.month, start.day, start.hour, start.minute, start.second, start.microsecond, tzinfo=start.tzinfo, ) else: _start = date(start.year, start.month, start.day) _end: pendulum.DateTime | pendulum.Date | datetime | date if not isinstance(end, pendulum.Date): if isinstance(end, datetime): end = pendulum.instance(end) else: end = pendulum.date(end.year, end.month, end.day) _end = end else: if isinstance(end, pendulum.DateTime): _end = datetime( end.year, end.month, end.day, end.hour, end.minute, end.second, end.microsecond, tzinfo=end.tzinfo, ) else: _end = date(end.year, end.month, end.day) self._invert = False if start > end: self._invert = True if absolute: end, start = start, end _end, _start = _start, _end self._absolute = absolute self._start: pendulum.DateTime | pendulum.Date = start self._end: pendulum.DateTime | pendulum.Date = end self._delta: PreciseDiff = precise_diff(_start, _end) @property def years(self) -> int: return self._delta.years @property def months(self) -> int: return self._delta.months @property def weeks(self) -> int: return abs(self._delta.days) // 7 * self._sign(self._delta.days) @property def days(self) -> int: return self._days @property def remaining_days(self) -> int: return abs(self._delta.days) % 7 * self._sign(self._days) @property def hours(self) -> int: return self._delta.hours @property def minutes(self) -> int: return self._delta.minutes @property def start(self) -> pendulum.DateTime | pendulum.Date | datetime | date: return self._start @property def end(self) -> pendulum.DateTime | pendulum.Date | datetime | date: return self._end def in_years(self) -> int: """ Gives the duration of the Interval in full years. """ return self.years def in_months(self) -> int: """ Gives the duration of the Interval in full months. """ return self.years * MONTHS_PER_YEAR + self.months def in_weeks(self) -> int: days = self.in_days() sign = 1 if days < 0: sign = -1 return sign * (abs(days) // 7) def in_days(self) -> int: return self._delta.total_days def in_words(self, locale: str | None = None, separator: str = " ") -> str: """ Get the current interval in words in the current locale. Ex: 6 jours 23 heures 58 minutes :param locale: The locale to use. Defaults to current locale. :param separator: The separator to use between each unit """ from pendulum.locales.locale import Locale intervals = [ ("year", self.years), ("month", self.months), ("week", self.weeks), ("day", self.remaining_days), ("hour", self.hours), ("minute", self.minutes), ("second", self.remaining_seconds), ] loaded_locale: Locale = Locale.load(locale or pendulum.get_locale()) parts = [] for interval in intervals: unit, interval_count = interval if abs(interval_count) > 0: translation = loaded_locale.translation( f"units.{unit}.{loaded_locale.plural(abs(interval_count))}" ) parts.append(translation.format(interval_count)) if not parts: count: str | int = 0 if abs(self.microseconds) > 0: unit = f"units.second.{loaded_locale.plural(1)}" count = f"{abs(self.microseconds) / 1e6:.2f}" else: unit = f"units.microsecond.{loaded_locale.plural(0)}" translation = loaded_locale.translation(unit) parts.append(translation.format(count)) return separator.join(parts) def range( self, unit: str, amount: int = 1 ) -> Iterator[pendulum.DateTime | pendulum.Date]: method = "add" op = operator.le if not self._absolute and self.invert: method = "subtract" op = operator.ge start, end = self.start, self.end i = amount while op(start, end): yield cast(Union[pendulum.DateTime, pendulum.Date], start) start = getattr(self.start, method)(**{unit: i}) i += amount def as_duration(self) -> Duration: """ Return the Interval as a Duration. """ return Duration(seconds=self.total_seconds()) def __iter__(self) -> Iterator[pendulum.DateTime | pendulum.Date]: return self.range("days") def __contains__( self, item: datetime | date | pendulum.DateTime | pendulum.Date ) -> bool: return self.start <= item <= self.end def __add__(self, other: timedelta) -> Duration: # type: ignore[override] return self.as_duration().__add__(other) __radd__ = __add__ # type: ignore[assignment] def __sub__(self, other: timedelta) -> Duration: # type: ignore[override] return self.as_duration().__sub__(other) def __neg__(self) -> Self: return self.__class__(self.end, self.start, self._absolute) def __mul__(self, other: int | float) -> Duration: # type: ignore[override] return self.as_duration().__mul__(other) __rmul__ = __mul__ # type: ignore[assignment] @overload # type: ignore[override] def __floordiv__(self, other: timedelta) -> int: ... @overload def __floordiv__(self, other: int) -> Duration: ... def __floordiv__(self, other: int | timedelta) -> int | Duration: return self.as_duration().__floordiv__(other) __div__ = __floordiv__ # type: ignore[assignment] @overload # type: ignore[override] def __truediv__(self, other: timedelta) -> float: ... @overload def __truediv__(self, other: float) -> Duration: ... def __truediv__(self, other: float | timedelta) -> Duration | float: return self.as_duration().__truediv__(other) def __mod__(self, other: timedelta) -> Duration: # type: ignore[override] return self.as_duration().__mod__(other) def __divmod__(self, other: timedelta) -> tuple[int, Duration]: return self.as_duration().__divmod__(other) def __abs__(self) -> Self: return self.__class__(self.start, self.end, absolute=True) def __repr__(self) -> str: return f" {self._end}]>" def __str__(self) -> str: return self.__repr__() def _cmp(self, other: timedelta) -> int: # Only needed for PyPy assert isinstance(other, timedelta) if isinstance(other, Interval): other = other.as_timedelta() td = self.as_timedelta() return 0 if td == other else 1 if td > other else -1 def _getstate( self, protocol: SupportsIndex = 3 ) -> tuple[ pendulum.DateTime | pendulum.Date | datetime | date, pendulum.DateTime | pendulum.Date | datetime | date, bool, ]: start, end = self.start, self.end if self._invert and self._absolute: end, start = start, end return start, end, self._absolute def __reduce__( self, ) -> tuple[ type[Self], tuple[ pendulum.DateTime | pendulum.Date | datetime | date, pendulum.DateTime | pendulum.Date | datetime | date, bool, ], ]: return self.__reduce_ex__(2) def __reduce_ex__( self, protocol: SupportsIndex ) -> tuple[ type[Self], tuple[ pendulum.DateTime | pendulum.Date | datetime | date, pendulum.DateTime | pendulum.Date | datetime | date, bool, ], ]: return self.__class__, self._getstate(protocol) def __hash__(self) -> int: return hash((self.start, self.end, self._absolute)) def __eq__(self, other: object) -> bool: if isinstance(other, Interval): return (self.start, self.end, self._absolute) == ( other.start, other.end, other._absolute, ) else: return self.as_duration() == other def __ne__(self, other: object) -> bool: return not self.__eq__(other) pendulum-3.0.0/src/pendulum/locales/000077500000000000000000000000001453741033500174125ustar00rootroot00000000000000pendulum-3.0.0/src/pendulum/locales/__init__.py000066400000000000000000000000001453741033500215110ustar00rootroot00000000000000pendulum-3.0.0/src/pendulum/locales/cs/000077500000000000000000000000001453741033500200175ustar00rootroot00000000000000pendulum-3.0.0/src/pendulum/locales/cs/__init__.py000066400000000000000000000000001453741033500221160ustar00rootroot00000000000000pendulum-3.0.0/src/pendulum/locales/cs/custom.py000066400000000000000000000010701453741033500217010ustar00rootroot00000000000000""" cs custom locale file. """ from __future__ import annotations translations = { "units": {"few_second": "pár vteÅ™in"}, # Relative time "ago": "{} zpÄ›t", "from_now": "za {}", "after": "{0} po", "before": "{0} zpÄ›t", # Ordinals "ordinal": {"one": ".", "two": ".", "few": ".", "other": "."}, # Date formats "date_formats": { "LTS": "h:mm:ss", "LT": "h:mm", "L": "DD. M. YYYY", "LL": "D. MMMM, YYYY", "LLL": "D. MMMM, YYYY h:mm", "LLLL": "dddd, D. MMMM, YYYY h:mm", }, } pendulum-3.0.0/src/pendulum/locales/cs/locale.py000066400000000000000000000177661453741033500216510ustar00rootroot00000000000000from __future__ import annotations from pendulum.locales.cs.custom import translations as custom_translations """ cs locale file. It has been generated automatically and must not be modified directly. """ locale = { "plural": lambda n: "few" if ((n == n and (n >= 2 and n <= 4)) and (0 == 0 and (0 == 0))) else "many" if (not (0 == 0 and (0 == 0))) else "one" if ((n == n and (n == 1)) and (0 == 0 and (0 == 0))) else "other", "ordinal": lambda n: "other", "translations": { "days": { "abbreviated": { 0: "po", 1: "út", 2: "st", 3: "Ät", 4: "pá", 5: "so", 6: "ne", }, "narrow": { 0: "P", 1: "Ú", 2: "S", 3: "ÄŒ", 4: "P", 5: "S", 6: "N", }, "short": { 0: "po", 1: "út", 2: "st", 3: "Ät", 4: "pá", 5: "so", 6: "ne", }, "wide": { 0: "pondÄ›lí", 1: "úterý", 2: "stÅ™eda", 3: "Ätvrtek", 4: "pátek", 5: "sobota", 6: "nedÄ›le", }, }, "months": { "abbreviated": { 1: "led", 2: "úno", 3: "bÅ™e", 4: "dub", 5: "kvÄ›", 6: "Ävn", 7: "Ävc", 8: "srp", 9: "zář", 10: "říj", 11: "lis", 12: "pro", }, "narrow": { 1: "1", 2: "2", 3: "3", 4: "4", 5: "5", 6: "6", 7: "7", 8: "8", 9: "9", 10: "10", 11: "11", 12: "12", }, "wide": { 1: "ledna", 2: "února", 3: "bÅ™ezna", 4: "dubna", 5: "kvÄ›tna", 6: "Äervna", 7: "Äervence", 8: "srpna", 9: "září", 10: "října", 11: "listopadu", 12: "prosince", }, }, "units": { "year": { "one": "{0} rok", "few": "{0} roky", "many": "{0} roku", "other": "{0} let", }, "month": { "one": "{0} mÄ›síc", "few": "{0} mÄ›síce", "many": "{0} mÄ›síce", "other": "{0} mÄ›síců", }, "week": { "one": "{0} týden", "few": "{0} týdny", "many": "{0} týdne", "other": "{0} týdnů", }, "day": { "one": "{0} den", "few": "{0} dny", "many": "{0} dne", "other": "{0} dní", }, "hour": { "one": "{0} hodina", "few": "{0} hodiny", "many": "{0} hodiny", "other": "{0} hodin", }, "minute": { "one": "{0} minuta", "few": "{0} minuty", "many": "{0} minuty", "other": "{0} minut", }, "second": { "one": "{0} sekunda", "few": "{0} sekundy", "many": "{0} sekundy", "other": "{0} sekund", }, "microsecond": { "one": "{0} mikrosekunda", "few": "{0} mikrosekundy", "many": "{0} mikrosekundy", "other": "{0} mikrosekund", }, }, "relative": { "year": { "future": { "other": "za {0} let", "one": "za {0} rok", "few": "za {0} roky", "many": "za {0} roku", }, "past": { "other": "pÅ™ed {0} lety", "one": "pÅ™ed {0} rokem", "few": "pÅ™ed {0} lety", "many": "pÅ™ed {0} roku", }, }, "month": { "future": { "other": "za {0} mÄ›síců", "one": "za {0} mÄ›síc", "few": "za {0} mÄ›síce", "many": "za {0} mÄ›síce", }, "past": { "other": "pÅ™ed {0} mÄ›síci", "one": "pÅ™ed {0} mÄ›sícem", "few": "pÅ™ed {0} mÄ›síci", "many": "pÅ™ed {0} mÄ›síce", }, }, "week": { "future": { "other": "za {0} týdnů", "one": "za {0} týden", "few": "za {0} týdny", "many": "za {0} týdne", }, "past": { "other": "pÅ™ed {0} týdny", "one": "pÅ™ed {0} týdnem", "few": "pÅ™ed {0} týdny", "many": "pÅ™ed {0} týdne", }, }, "day": { "future": { "other": "za {0} dní", "one": "za {0} den", "few": "za {0} dny", "many": "za {0} dne", }, "past": { "other": "pÅ™ed {0} dny", "one": "pÅ™ed {0} dnem", "few": "pÅ™ed {0} dny", "many": "pÅ™ed {0} dne", }, }, "hour": { "future": { "other": "za {0} hodin", "one": "za {0} hodinu", "few": "za {0} hodiny", "many": "za {0} hodiny", }, "past": { "other": "pÅ™ed {0} hodinami", "one": "pÅ™ed {0} hodinou", "few": "pÅ™ed {0} hodinami", "many": "pÅ™ed {0} hodiny", }, }, "minute": { "future": { "other": "za {0} minut", "one": "za {0} minutu", "few": "za {0} minuty", "many": "za {0} minuty", }, "past": { "other": "pÅ™ed {0} minutami", "one": "pÅ™ed {0} minutou", "few": "pÅ™ed {0} minutami", "many": "pÅ™ed {0} minuty", }, }, "second": { "future": { "other": "za {0} sekund", "one": "za {0} sekundu", "few": "za {0} sekundy", "many": "za {0} sekundy", }, "past": { "other": "pÅ™ed {0} sekundami", "one": "pÅ™ed {0} sekundou", "few": "pÅ™ed {0} sekundami", "many": "pÅ™ed {0} sekundy", }, }, }, "day_periods": { "midnight": "půlnoc", "am": "dop.", "noon": "poledne", "pm": "odp.", "morning1": "ráno", "morning2": "dopoledne", "afternoon1": "odpoledne", "evening1": "veÄer", "night1": "v noci", }, "week_data": { "min_days": 1, "first_day": 0, "weekend_start": 5, "weekend_end": 6, }, }, "custom": custom_translations, } pendulum-3.0.0/src/pendulum/locales/da/000077500000000000000000000000001453741033500177765ustar00rootroot00000000000000pendulum-3.0.0/src/pendulum/locales/da/__init__.py000066400000000000000000000000001453741033500220750ustar00rootroot00000000000000pendulum-3.0.0/src/pendulum/locales/da/custom.py000066400000000000000000000006171453741033500216660ustar00rootroot00000000000000""" da custom locale file. """ from __future__ import annotations translations = { # Relative time "after": "{0} efter", "before": "{0} før", # Date formats "date_formats": { "LTS": "HH:mm:ss", "LT": "HH:mm", "LLLL": "dddd [d.] D. MMMM YYYY HH:mm", "LLL": "D. MMMM YYYY HH:mm", "LL": "D. MMMM YYYY", "L": "DD/MM/YYYY", }, } pendulum-3.0.0/src/pendulum/locales/da/locale.py000066400000000000000000000115241453741033500216120ustar00rootroot00000000000000from __future__ import annotations from pendulum.locales.da.custom import translations as custom_translations """ da locale file. It has been generated automatically and must not be modified directly. """ locale = { "plural": lambda n: "one" if ( (n == n and (n == 1)) or ((not (0 == 0 and (0 == 0))) and (n == n and ((n == 0) or (n == 1)))) ) else "other", "ordinal": lambda n: "other", "translations": { "days": { "abbreviated": { 0: "man.", 1: "tir.", 2: "ons.", 3: "tor.", 4: "fre.", 5: "lør.", 6: "søn.", }, "narrow": {0: "M", 1: "T", 2: "O", 3: "T", 4: "F", 5: "L", 6: "S"}, "short": {0: "ma", 1: "ti", 2: "on", 3: "to", 4: "fr", 5: "lø", 6: "sø"}, "wide": { 0: "mandag", 1: "tirsdag", 2: "onsdag", 3: "torsdag", 4: "fredag", 5: "lørdag", 6: "søndag", }, }, "months": { "abbreviated": { 1: "jan.", 2: "feb.", 3: "mar.", 4: "apr.", 5: "maj", 6: "jun.", 7: "jul.", 8: "aug.", 9: "sep.", 10: "okt.", 11: "nov.", 12: "dec.", }, "narrow": { 1: "J", 2: "F", 3: "M", 4: "A", 5: "M", 6: "J", 7: "J", 8: "A", 9: "S", 10: "O", 11: "N", 12: "D", }, "wide": { 1: "januar", 2: "februar", 3: "marts", 4: "april", 5: "maj", 6: "juni", 7: "juli", 8: "august", 9: "september", 10: "oktober", 11: "november", 12: "december", }, }, "units": { "year": {"one": "{0} Ã¥r", "other": "{0} Ã¥r"}, "month": {"one": "{0} mÃ¥ned", "other": "{0} mÃ¥neder"}, "week": {"one": "{0} uge", "other": "{0} uger"}, "day": {"one": "{0} dag", "other": "{0} dage"}, "hour": {"one": "{0} time", "other": "{0} timer"}, "minute": {"one": "{0} minut", "other": "{0} minutter"}, "second": {"one": "{0} sekund", "other": "{0} sekunder"}, "microsecond": {"one": "{0} mikrosekund", "other": "{0} mikrosekunder"}, }, "relative": { "year": { "future": {"other": "om {0} Ã¥r", "one": "om {0} Ã¥r"}, "past": {"other": "for {0} Ã¥r siden", "one": "for {0} Ã¥r siden"}, }, "month": { "future": {"other": "om {0} mÃ¥neder", "one": "om {0} mÃ¥ned"}, "past": { "other": "for {0} mÃ¥neder siden", "one": "for {0} mÃ¥ned siden", }, }, "week": { "future": {"other": "om {0} uger", "one": "om {0} uge"}, "past": {"other": "for {0} uger siden", "one": "for {0} uge siden"}, }, "day": { "future": {"other": "om {0} dage", "one": "om {0} dag"}, "past": {"other": "for {0} dage siden", "one": "for {0} dag siden"}, }, "hour": { "future": {"other": "om {0} timer", "one": "om {0} time"}, "past": {"other": "for {0} timer siden", "one": "for {0} time siden"}, }, "minute": { "future": {"other": "om {0} minutter", "one": "om {0} minut"}, "past": { "other": "for {0} minutter siden", "one": "for {0} minut siden", }, }, "second": { "future": {"other": "om {0} sekunder", "one": "om {0} sekund"}, "past": { "other": "for {0} sekunder siden", "one": "for {0} sekund siden", }, }, }, "day_periods": { "midnight": "midnat", "am": "AM", "pm": "PM", "morning1": "om morgenen", "morning2": "om formiddagen", "afternoon1": "om eftermiddagen", "evening1": "om aftenen", "night1": "om natten", }, "week_data": { "min_days": 1, "first_day": 0, "weekend_start": 5, "weekend_end": 6, }, }, "custom": custom_translations, } pendulum-3.0.0/src/pendulum/locales/de/000077500000000000000000000000001453741033500200025ustar00rootroot00000000000000pendulum-3.0.0/src/pendulum/locales/de/__init__.py000066400000000000000000000000001453741033500221010ustar00rootroot00000000000000pendulum-3.0.0/src/pendulum/locales/de/custom.py000066400000000000000000000020531453741033500216660ustar00rootroot00000000000000""" de custom locale file. """ from __future__ import annotations translations = { # Relative time "after": "{0} später", "before": "{0} zuvor", "units_relative": { "year": { "future": {"one": "{0} Jahr", "other": "{0} Jahren"}, "past": {"one": "{0} Jahr", "other": "{0} Jahren"}, }, "month": { "future": {"one": "{0} Monat", "other": "{0} Monaten"}, "past": {"one": "{0} Monat", "other": "{0} Monaten"}, }, "week": { "future": {"one": "{0} Woche", "other": "{0} Wochen"}, "past": {"one": "{0} Woche", "other": "{0} Wochen"}, }, "day": { "future": {"one": "{0} Tag", "other": "{0} Tagen"}, "past": {"one": "{0} Tag", "other": "{0} Tagen"}, }, }, # Date formats "date_formats": { "LTS": "HH:mm:ss", "LT": "HH:mm", "LLLL": "dddd, D. MMMM YYYY HH:mm", "LLL": "D. MMMM YYYY HH:mm", "LL": "D. MMMM YYYY", "L": "DD.MM.YYYY", }, } pendulum-3.0.0/src/pendulum/locales/de/locale.py000066400000000000000000000113141453741033500216130ustar00rootroot00000000000000from __future__ import annotations from pendulum.locales.de.custom import translations as custom_translations """ de locale file. It has been generated automatically and must not be modified directly. """ locale = { "plural": lambda n: "one" if ((n == n and (n == 1)) and (0 == 0 and (0 == 0))) else "other", "ordinal": lambda n: "other", "translations": { "days": { "abbreviated": { 0: "Mo.", 1: "Di.", 2: "Mi.", 3: "Do.", 4: "Fr.", 5: "Sa.", 6: "So.", }, "narrow": {0: "M", 1: "D", 2: "M", 3: "D", 4: "F", 5: "S", 6: "S"}, "short": { 0: "Mo.", 1: "Di.", 2: "Mi.", 3: "Do.", 4: "Fr.", 5: "Sa.", 6: "So.", }, "wide": { 0: "Montag", 1: "Dienstag", 2: "Mittwoch", 3: "Donnerstag", 4: "Freitag", 5: "Samstag", 6: "Sonntag", }, }, "months": { "abbreviated": { 1: "Jan.", 2: "Feb.", 3: "März", 4: "Apr.", 5: "Mai", 6: "Juni", 7: "Juli", 8: "Aug.", 9: "Sep.", 10: "Okt.", 11: "Nov.", 12: "Dez.", }, "narrow": { 1: "J", 2: "F", 3: "M", 4: "A", 5: "M", 6: "J", 7: "J", 8: "A", 9: "S", 10: "O", 11: "N", 12: "D", }, "wide": { 1: "Januar", 2: "Februar", 3: "März", 4: "April", 5: "Mai", 6: "Juni", 7: "Juli", 8: "August", 9: "September", 10: "Oktober", 11: "November", 12: "Dezember", }, }, "units": { "year": {"one": "{0} Jahr", "other": "{0} Jahre"}, "month": {"one": "{0} Monat", "other": "{0} Monate"}, "week": {"one": "{0} Woche", "other": "{0} Wochen"}, "day": {"one": "{0} Tag", "other": "{0} Tage"}, "hour": {"one": "{0} Stunde", "other": "{0} Stunden"}, "minute": {"one": "{0} Minute", "other": "{0} Minuten"}, "second": {"one": "{0} Sekunde", "other": "{0} Sekunden"}, "microsecond": {"one": "{0} Mikrosekunde", "other": "{0} Mikrosekunden"}, }, "relative": { "year": { "future": {"other": "in {0} Jahren", "one": "in {0} Jahr"}, "past": {"other": "vor {0} Jahren", "one": "vor {0} Jahr"}, }, "month": { "future": {"other": "in {0} Monaten", "one": "in {0} Monat"}, "past": {"other": "vor {0} Monaten", "one": "vor {0} Monat"}, }, "week": { "future": {"other": "in {0} Wochen", "one": "in {0} Woche"}, "past": {"other": "vor {0} Wochen", "one": "vor {0} Woche"}, }, "day": { "future": {"other": "in {0} Tagen", "one": "in {0} Tag"}, "past": {"other": "vor {0} Tagen", "one": "vor {0} Tag"}, }, "hour": { "future": {"other": "in {0} Stunden", "one": "in {0} Stunde"}, "past": {"other": "vor {0} Stunden", "one": "vor {0} Stunde"}, }, "minute": { "future": {"other": "in {0} Minuten", "one": "in {0} Minute"}, "past": {"other": "vor {0} Minuten", "one": "vor {0} Minute"}, }, "second": { "future": {"other": "in {0} Sekunden", "one": "in {0} Sekunde"}, "past": {"other": "vor {0} Sekunden", "one": "vor {0} Sekunde"}, }, }, "day_periods": { "midnight": "Mitternacht", "am": "vorm.", "pm": "nachm.", "morning1": "morgens", "morning2": "vormittags", "afternoon1": "mittags", "afternoon2": "nachmittags", "evening1": "abends", "night1": "nachts", }, "week_data": { "min_days": 1, "first_day": 0, "weekend_start": 5, "weekend_end": 6, }, }, "custom": custom_translations, } pendulum-3.0.0/src/pendulum/locales/en/000077500000000000000000000000001453741033500200145ustar00rootroot00000000000000pendulum-3.0.0/src/pendulum/locales/en/__init__.py000066400000000000000000000000001453741033500221130ustar00rootroot00000000000000pendulum-3.0.0/src/pendulum/locales/en/custom.py000066400000000000000000000011031453741033500216730ustar00rootroot00000000000000""" en custom locale file. """ from __future__ import annotations translations = { "units": {"few_second": "a few seconds"}, # Relative time "ago": "{} ago", "from_now": "in {}", "after": "{0} after", "before": "{0} before", # Ordinals "ordinal": {"one": "st", "two": "nd", "few": "rd", "other": "th"}, # Date formats "date_formats": { "LTS": "h:mm:ss A", "LT": "h:mm A", "L": "MM/DD/YYYY", "LL": "MMMM D, YYYY", "LLL": "MMMM D, YYYY h:mm A", "LLLL": "dddd, MMMM D, YYYY h:mm A", }, } pendulum-3.0.0/src/pendulum/locales/en/locale.py000066400000000000000000000116541453741033500216340ustar00rootroot00000000000000from __future__ import annotations from pendulum.locales.en.custom import translations as custom_translations """ en locale file. It has been generated automatically and must not be modified directly. """ locale = { "plural": lambda n: "one" if ((n == n and (n == 1)) and (0 == 0 and (0 == 0))) else "other", "ordinal": lambda n: "few" if ( ((n % 10) == (n % 10) and ((n % 10) == 3)) and (not ((n % 100) == (n % 100) and ((n % 100) == 13))) ) else "one" if ( ((n % 10) == (n % 10) and ((n % 10) == 1)) and (not ((n % 100) == (n % 100) and ((n % 100) == 11))) ) else "two" if ( ((n % 10) == (n % 10) and ((n % 10) == 2)) and (not ((n % 100) == (n % 100) and ((n % 100) == 12))) ) else "other", "translations": { "days": { "abbreviated": { 0: "Mon", 1: "Tue", 2: "Wed", 3: "Thu", 4: "Fri", 5: "Sat", 6: "Sun", }, "narrow": {0: "M", 1: "T", 2: "W", 3: "T", 4: "F", 5: "S", 6: "S"}, "short": {0: "Mo", 1: "Tu", 2: "We", 3: "Th", 4: "Fr", 5: "Sa", 6: "Su"}, "wide": { 0: "Monday", 1: "Tuesday", 2: "Wednesday", 3: "Thursday", 4: "Friday", 5: "Saturday", 6: "Sunday", }, }, "months": { "abbreviated": { 1: "Jan", 2: "Feb", 3: "Mar", 4: "Apr", 5: "May", 6: "Jun", 7: "Jul", 8: "Aug", 9: "Sep", 10: "Oct", 11: "Nov", 12: "Dec", }, "narrow": { 1: "J", 2: "F", 3: "M", 4: "A", 5: "M", 6: "J", 7: "J", 8: "A", 9: "S", 10: "O", 11: "N", 12: "D", }, "wide": { 1: "January", 2: "February", 3: "March", 4: "April", 5: "May", 6: "June", 7: "July", 8: "August", 9: "September", 10: "October", 11: "November", 12: "December", }, }, "units": { "year": {"one": "{0} year", "other": "{0} years"}, "month": {"one": "{0} month", "other": "{0} months"}, "week": {"one": "{0} week", "other": "{0} weeks"}, "day": {"one": "{0} day", "other": "{0} days"}, "hour": {"one": "{0} hour", "other": "{0} hours"}, "minute": {"one": "{0} minute", "other": "{0} minutes"}, "second": {"one": "{0} second", "other": "{0} seconds"}, "microsecond": {"one": "{0} microsecond", "other": "{0} microseconds"}, }, "relative": { "year": { "future": {"other": "in {0} years", "one": "in {0} year"}, "past": {"other": "{0} years ago", "one": "{0} year ago"}, }, "month": { "future": {"other": "in {0} months", "one": "in {0} month"}, "past": {"other": "{0} months ago", "one": "{0} month ago"}, }, "week": { "future": {"other": "in {0} weeks", "one": "in {0} week"}, "past": {"other": "{0} weeks ago", "one": "{0} week ago"}, }, "day": { "future": {"other": "in {0} days", "one": "in {0} day"}, "past": {"other": "{0} days ago", "one": "{0} day ago"}, }, "hour": { "future": {"other": "in {0} hours", "one": "in {0} hour"}, "past": {"other": "{0} hours ago", "one": "{0} hour ago"}, }, "minute": { "future": {"other": "in {0} minutes", "one": "in {0} minute"}, "past": {"other": "{0} minutes ago", "one": "{0} minute ago"}, }, "second": { "future": {"other": "in {0} seconds", "one": "in {0} second"}, "past": {"other": "{0} seconds ago", "one": "{0} second ago"}, }, }, "day_periods": { "midnight": "midnight", "am": "AM", "noon": "noon", "pm": "PM", "morning1": "in the morning", "afternoon1": "in the afternoon", "evening1": "in the evening", "night1": "at night", }, "week_data": { "min_days": 1, "first_day": 6, "weekend_start": 5, "weekend_end": 6, }, }, "custom": custom_translations, } pendulum-3.0.0/src/pendulum/locales/en_gb/000077500000000000000000000000001453741033500204645ustar00rootroot00000000000000pendulum-3.0.0/src/pendulum/locales/en_gb/__init__.py000066400000000000000000000000001453741033500225630ustar00rootroot00000000000000pendulum-3.0.0/src/pendulum/locales/en_gb/custom.py000066400000000000000000000010771453741033500223550ustar00rootroot00000000000000""" en-gb custom locale file. """ from __future__ import annotations translations = { "units": {"few_second": "a few seconds"}, # Relative time "ago": "{} ago", "from_now": "in {}", "after": "{0} after", "before": "{0} before", # Ordinals "ordinal": {"one": "st", "two": "nd", "few": "rd", "other": "th"}, # Date formats "date_formats": { "LTS": "HH:mm:ss", "LT": "HH:mm", "L": "DD/MM/YYYY", "LL": "D MMMM YYYY", "LLL": "D MMMM YYYY HH:mm", "LLLL": "dddd, D MMMM YYYY HH:mm", }, } pendulum-3.0.0/src/pendulum/locales/en_gb/locale.py000066400000000000000000000145431453741033500223040ustar00rootroot00000000000000from __future__ import annotations from pendulum.locales.en_gb.custom import translations as custom_translations """ en-gb locale file. It has been generated automatically and must not be modified directly. """ locale = { "plural": lambda n: "one" if ((n == n and (n == 1)) and (0 == 0 and (0 == 0))) else "other", "ordinal": lambda n: "few" if ( ((n % 10) == (n % 10) and ((n % 10) == 3)) and (not ((n % 100) == (n % 100) and ((n % 100) == 13))) ) else "one" if ( ((n % 10) == (n % 10) and ((n % 10) == 1)) and (not ((n % 100) == (n % 100) and ((n % 100) == 11))) ) else "two" if ( ((n % 10) == (n % 10) and ((n % 10) == 2)) and (not ((n % 100) == (n % 100) and ((n % 100) == 12))) ) else "other", "translations": { "days": { "abbreviated": { 0: "Mon", 1: "Tue", 2: "Wed", 3: "Thu", 4: "Fri", 5: "Sat", 6: "Sun", }, "narrow": { 0: "M", 1: "T", 2: "W", 3: "T", 4: "F", 5: "S", 6: "S", }, "short": { 0: "Mo", 1: "Tu", 2: "We", 3: "Th", 4: "Fr", 5: "Sa", 6: "Su", }, "wide": { 0: "Monday", 1: "Tuesday", 2: "Wednesday", 3: "Thursday", 4: "Friday", 5: "Saturday", 6: "Sunday", }, }, "months": { "abbreviated": { 1: "Jan", 2: "Feb", 3: "Mar", 4: "Apr", 5: "May", 6: "Jun", 7: "Jul", 8: "Aug", 9: "Sept", 10: "Oct", 11: "Nov", 12: "Dec", }, "narrow": { 1: "J", 2: "F", 3: "M", 4: "A", 5: "M", 6: "J", 7: "J", 8: "A", 9: "S", 10: "O", 11: "N", 12: "D", }, "wide": { 1: "January", 2: "February", 3: "March", 4: "April", 5: "May", 6: "June", 7: "July", 8: "August", 9: "September", 10: "October", 11: "November", 12: "December", }, }, "units": { "year": { "one": "{0} year", "other": "{0} years", }, "month": { "one": "{0} month", "other": "{0} months", }, "week": { "one": "{0} week", "other": "{0} weeks", }, "day": { "one": "{0} day", "other": "{0} days", }, "hour": { "one": "{0} hour", "other": "{0} hours", }, "minute": { "one": "{0} minute", "other": "{0} minutes", }, "second": { "one": "{0} second", "other": "{0} seconds", }, "microsecond": { "one": "{0} microsecond", "other": "{0} microseconds", }, }, "relative": { "year": { "future": { "other": "in {0} years", "one": "in {0} year", }, "past": { "other": "{0} years ago", "one": "{0} year ago", }, }, "month": { "future": { "other": "in {0} months", "one": "in {0} month", }, "past": { "other": "{0} months ago", "one": "{0} month ago", }, }, "week": { "future": { "other": "in {0} weeks", "one": "in {0} week", }, "past": { "other": "{0} weeks ago", "one": "{0} week ago", }, }, "day": { "future": { "other": "in {0} days", "one": "in {0} day", }, "past": { "other": "{0} days ago", "one": "{0} day ago", }, }, "hour": { "future": { "other": "in {0} hours", "one": "in {0} hour", }, "past": { "other": "{0} hours ago", "one": "{0} hour ago", }, }, "minute": { "future": { "other": "in {0} minutes", "one": "in {0} minute", }, "past": { "other": "{0} minutes ago", "one": "{0} minute ago", }, }, "second": { "future": { "other": "in {0} seconds", "one": "in {0} second", }, "past": { "other": "{0} seconds ago", "one": "{0} second ago", }, }, }, "day_periods": { "midnight": "midnight", "am": "am", "noon": "noon", "pm": "pm", "morning1": "in the morning", "afternoon1": "in the afternoon", "evening1": "in the evening", "night1": "at night", }, "week_data": { "min_days": 4, "first_day": 0, "weekend_start": 5, "weekend_end": 6, }, }, "custom": custom_translations, } pendulum-3.0.0/src/pendulum/locales/en_us/000077500000000000000000000000001453741033500205235ustar00rootroot00000000000000pendulum-3.0.0/src/pendulum/locales/en_us/__init__.py000066400000000000000000000000001453741033500226220ustar00rootroot00000000000000pendulum-3.0.0/src/pendulum/locales/en_us/custom.py000066400000000000000000000011061453741033500224050ustar00rootroot00000000000000""" en-us custom locale file. """ from __future__ import annotations translations = { "units": {"few_second": "a few seconds"}, # Relative time "ago": "{} ago", "from_now": "in {}", "after": "{0} after", "before": "{0} before", # Ordinals "ordinal": {"one": "st", "two": "nd", "few": "rd", "other": "th"}, # Date formats "date_formats": { "LTS": "h:mm:ss A", "LT": "h:mm A", "L": "MM/DD/YYYY", "LL": "MMMM D, YYYY", "LLL": "MMMM D, YYYY h:mm A", "LLLL": "dddd, MMMM D, YYYY h:mm A", }, } pendulum-3.0.0/src/pendulum/locales/en_us/locale.py000066400000000000000000000145421453741033500223420ustar00rootroot00000000000000from __future__ import annotations from pendulum.locales.en_us.custom import translations as custom_translations """ en-us locale file. It has been generated automatically and must not be modified directly. """ locale = { "plural": lambda n: "one" if ((n == n and (n == 1)) and (0 == 0 and (0 == 0))) else "other", "ordinal": lambda n: "few" if ( ((n % 10) == (n % 10) and ((n % 10) == 3)) and (not ((n % 100) == (n % 100) and ((n % 100) == 13))) ) else "one" if ( ((n % 10) == (n % 10) and ((n % 10) == 1)) and (not ((n % 100) == (n % 100) and ((n % 100) == 11))) ) else "two" if ( ((n % 10) == (n % 10) and ((n % 10) == 2)) and (not ((n % 100) == (n % 100) and ((n % 100) == 12))) ) else "other", "translations": { "days": { "abbreviated": { 0: "Mon", 1: "Tue", 2: "Wed", 3: "Thu", 4: "Fri", 5: "Sat", 6: "Sun", }, "narrow": { 0: "M", 1: "T", 2: "W", 3: "T", 4: "F", 5: "S", 6: "S", }, "short": { 0: "Mo", 1: "Tu", 2: "We", 3: "Th", 4: "Fr", 5: "Sa", 6: "Su", }, "wide": { 0: "Monday", 1: "Tuesday", 2: "Wednesday", 3: "Thursday", 4: "Friday", 5: "Saturday", 6: "Sunday", }, }, "months": { "abbreviated": { 1: "Jan", 2: "Feb", 3: "Mar", 4: "Apr", 5: "May", 6: "Jun", 7: "Jul", 8: "Aug", 9: "Sep", 10: "Oct", 11: "Nov", 12: "Dec", }, "narrow": { 1: "J", 2: "F", 3: "M", 4: "A", 5: "M", 6: "J", 7: "J", 8: "A", 9: "S", 10: "O", 11: "N", 12: "D", }, "wide": { 1: "January", 2: "February", 3: "March", 4: "April", 5: "May", 6: "June", 7: "July", 8: "August", 9: "September", 10: "October", 11: "November", 12: "December", }, }, "units": { "year": { "one": "{0} year", "other": "{0} years", }, "month": { "one": "{0} month", "other": "{0} months", }, "week": { "one": "{0} week", "other": "{0} weeks", }, "day": { "one": "{0} day", "other": "{0} days", }, "hour": { "one": "{0} hour", "other": "{0} hours", }, "minute": { "one": "{0} minute", "other": "{0} minutes", }, "second": { "one": "{0} second", "other": "{0} seconds", }, "microsecond": { "one": "{0} microsecond", "other": "{0} microseconds", }, }, "relative": { "year": { "future": { "other": "in {0} years", "one": "in {0} year", }, "past": { "other": "{0} years ago", "one": "{0} year ago", }, }, "month": { "future": { "other": "in {0} months", "one": "in {0} month", }, "past": { "other": "{0} months ago", "one": "{0} month ago", }, }, "week": { "future": { "other": "in {0} weeks", "one": "in {0} week", }, "past": { "other": "{0} weeks ago", "one": "{0} week ago", }, }, "day": { "future": { "other": "in {0} days", "one": "in {0} day", }, "past": { "other": "{0} days ago", "one": "{0} day ago", }, }, "hour": { "future": { "other": "in {0} hours", "one": "in {0} hour", }, "past": { "other": "{0} hours ago", "one": "{0} hour ago", }, }, "minute": { "future": { "other": "in {0} minutes", "one": "in {0} minute", }, "past": { "other": "{0} minutes ago", "one": "{0} minute ago", }, }, "second": { "future": { "other": "in {0} seconds", "one": "in {0} second", }, "past": { "other": "{0} seconds ago", "one": "{0} second ago", }, }, }, "day_periods": { "midnight": "midnight", "am": "AM", "noon": "noon", "pm": "PM", "morning1": "in the morning", "afternoon1": "in the afternoon", "evening1": "in the evening", "night1": "at night", }, "week_data": { "min_days": 1, "first_day": 6, "weekend_start": 5, "weekend_end": 6, }, }, "custom": custom_translations, } pendulum-3.0.0/src/pendulum/locales/es/000077500000000000000000000000001453741033500200215ustar00rootroot00000000000000pendulum-3.0.0/src/pendulum/locales/es/__init__.py000066400000000000000000000000001453741033500221200ustar00rootroot00000000000000pendulum-3.0.0/src/pendulum/locales/es/custom.py000066400000000000000000000010731453741033500217060ustar00rootroot00000000000000""" es custom locale file. """ from __future__ import annotations translations = { "units": {"few_second": "unos segundos"}, # Relative time "ago": "hace {0}", "from_now": "dentro de {0}", "after": "{0} después", "before": "{0} antes", # Ordinals "ordinal": {"other": "º"}, # Date formats "date_formats": { "LTS": "H:mm:ss", "LT": "H:mm", "LLLL": "dddd, D [de] MMMM [de] YYYY H:mm", "LLL": "D [de] MMMM [de] YYYY H:mm", "LL": "D [de] MMMM [de] YYYY", "L": "DD/MM/YYYY", }, } pendulum-3.0.0/src/pendulum/locales/es/locale.py000066400000000000000000000114021453741033500216300ustar00rootroot00000000000000from __future__ import annotations from pendulum.locales.es.custom import translations as custom_translations """ es locale file. It has been generated automatically and must not be modified directly. """ locale = { "plural": lambda n: "one" if (n == n and (n == 1)) else "other", "ordinal": lambda n: "other", "translations": { "days": { "abbreviated": { 0: "lun.", 1: "mar.", 2: "mié.", 3: "jue.", 4: "vie.", 5: "sáb.", 6: "dom.", }, "narrow": {0: "L", 1: "M", 2: "X", 3: "J", 4: "V", 5: "S", 6: "D"}, "short": {0: "LU", 1: "MA", 2: "MI", 3: "JU", 4: "VI", 5: "SA", 6: "DO"}, "wide": { 0: "lunes", 1: "martes", 2: "miércoles", 3: "jueves", 4: "viernes", 5: "sábado", 6: "domingo", }, }, "months": { "abbreviated": { 1: "ene.", 2: "feb.", 3: "mar.", 4: "abr.", 5: "may.", 6: "jun.", 7: "jul.", 8: "ago.", 9: "sept.", 10: "oct.", 11: "nov.", 12: "dic.", }, "narrow": { 1: "E", 2: "F", 3: "M", 4: "A", 5: "M", 6: "J", 7: "J", 8: "A", 9: "S", 10: "O", 11: "N", 12: "D", }, "wide": { 1: "enero", 2: "febrero", 3: "marzo", 4: "abril", 5: "mayo", 6: "junio", 7: "julio", 8: "agosto", 9: "septiembre", 10: "octubre", 11: "noviembre", 12: "diciembre", }, }, "units": { "year": {"one": "{0} año", "other": "{0} años"}, "month": {"one": "{0} mes", "other": "{0} meses"}, "week": {"one": "{0} semana", "other": "{0} semanas"}, "day": {"one": "{0} día", "other": "{0} días"}, "hour": {"one": "{0} hora", "other": "{0} horas"}, "minute": {"one": "{0} minuto", "other": "{0} minutos"}, "second": {"one": "{0} segundo", "other": "{0} segundos"}, "microsecond": {"one": "{0} microsegundo", "other": "{0} microsegundos"}, }, "relative": { "year": { "future": {"other": "dentro de {0} años", "one": "dentro de {0} año"}, "past": {"other": "hace {0} años", "one": "hace {0} año"}, }, "month": { "future": {"other": "dentro de {0} meses", "one": "dentro de {0} mes"}, "past": {"other": "hace {0} meses", "one": "hace {0} mes"}, }, "week": { "future": { "other": "dentro de {0} semanas", "one": "dentro de {0} semana", }, "past": {"other": "hace {0} semanas", "one": "hace {0} semana"}, }, "day": { "future": {"other": "dentro de {0} días", "one": "dentro de {0} día"}, "past": {"other": "hace {0} días", "one": "hace {0} día"}, }, "hour": { "future": {"other": "dentro de {0} horas", "one": "dentro de {0} hora"}, "past": {"other": "hace {0} horas", "one": "hace {0} hora"}, }, "minute": { "future": { "other": "dentro de {0} minutos", "one": "dentro de {0} minuto", }, "past": {"other": "hace {0} minutos", "one": "hace {0} minuto"}, }, "second": { "future": { "other": "dentro de {0} segundos", "one": "dentro de {0} segundo", }, "past": {"other": "hace {0} segundos", "one": "hace {0} segundo"}, }, }, "day_periods": { "am": "a. m.", "noon": "del mediodía", "pm": "p. m.", "morning1": "de la madrugada", "morning2": "de la mañana", "evening1": "de la tarde", "night1": "de la noche", }, "week_data": { "min_days": 1, "first_day": 0, "weekend_start": 5, "weekend_end": 6, }, }, "custom": custom_translations, } pendulum-3.0.0/src/pendulum/locales/fa/000077500000000000000000000000001453741033500200005ustar00rootroot00000000000000pendulum-3.0.0/src/pendulum/locales/fa/__init__.py000066400000000000000000000000001453741033500220770ustar00rootroot00000000000000pendulum-3.0.0/src/pendulum/locales/fa/custom.py000066400000000000000000000006231453741033500216650ustar00rootroot00000000000000""" fa custom locale file. """ from __future__ import annotations translations = { # Relative time "after": "{0} پس از", "before": "{0} پیش از", # Date formats "date_formats": { "LTS": "HH:mm:ss", "LT": "HH:mm", "LLLL": "dddd, D MMMM YYYY HH:mm", "LLL": "D MMMM YYYY HH:mm", "LL": "D MMMM YYYY", "L": "DD/MM/YYYY", }, } pendulum-3.0.0/src/pendulum/locales/fa/locale.py000066400000000000000000000117671453741033500216250ustar00rootroot00000000000000from __future__ import annotations from pendulum.locales.fa.custom import translations as custom_translations """ fa locale file. It has been generated automatically and must not be modified directly. """ locale = { "plural": lambda n: "one" if ((n == n and (n == 0)) or (n == n and (n == 1))) else "other", "ordinal": lambda n: "other", "translations": { "days": { "abbreviated": { 0: "دوشنبه", 1: "سه\u200cشنبه", 2: "چهارشنبه", 3: "پنجشنبه", 4: "جمعه", 5: "شنبه", 6: "یکشنبه", }, "narrow": {0: "د", 1: "س", 2: "Ú†", 3: "Ù¾", 4: "ج", 5: "Ø´", 6: "ÛŒ"}, "short": {0: "۲ش", 1: "۳ش", 2: "Û´Ø´", 3: "۵ش", 4: "ج", 5: "Ø´", 6: "۱ش"}, "wide": { 0: "دوشنبه", 1: "سه\u200cشنبه", 2: "چهارشنبه", 3: "پنجشنبه", 4: "جمعه", 5: "شنبه", 6: "یکشنبه", }, }, "months": { "abbreviated": { 1: "ژانویهٔ", 2: "Ùوریهٔ", 3: "مارس", 4: "آوریل", 5: "مهٔ", 6: "ژوئن", 7: "ژوئیهٔ", 8: "اوت", 9: "سپتامبر", 10: "اکتبر", 11: "نوامبر", 12: "دسامبر", }, "narrow": { 1: "Ú˜", 2: "Ù", 3: "Ù…", 4: "Ø¢", 5: "Ù…", 6: "Ú˜", 7: "Ú˜", 8: "ا", 9: "س", 10: "ا", 11: "Ù†", 12: "د", }, "wide": { 1: "ژانویهٔ", 2: "Ùوریهٔ", 3: "مارس", 4: "آوریل", 5: "مهٔ", 6: "ژوئن", 7: "ژوئیهٔ", 8: "اوت", 9: "سپتامبر", 10: "اکتبر", 11: "نوامبر", 12: "دسامبر", }, }, "units": { "year": {"one": "{0} سال", "other": "{0} سال"}, "month": {"one": "{0} ماه", "other": "{0} ماه"}, "week": {"one": "{0} Ù‡ÙØªÙ‡", "other": "{0} Ù‡ÙØªÙ‡"}, "day": {"one": "{0} روز", "other": "{0} روز"}, "hour": {"one": "{0} ساعت", "other": "{0} ساعت"}, "minute": {"one": "{0} دقیقه", "other": "{0} دقیقه"}, "second": {"one": "{0} ثانیه", "other": "{0} ثانیه"}, "microsecond": {"one": "{0} میکروثانیه", "other": "{0} میکروثانیه"}, }, "relative": { "year": { "future": {"other": "{0} سال بعد", "one": "{0} سال بعد"}, "past": {"other": "{0} سال پیش", "one": "{0} سال پیش"}, }, "month": { "future": {"other": "{0} ماه بعد", "one": "{0} ماه بعد"}, "past": {"other": "{0} ماه پیش", "one": "{0} ماه پیش"}, }, "week": { "future": {"other": "{0} Ù‡ÙØªÙ‡ بعد", "one": "{0} Ù‡ÙØªÙ‡ بعد"}, "past": {"other": "{0} Ù‡ÙØªÙ‡ پیش", "one": "{0} Ù‡ÙØªÙ‡ پیش"}, }, "day": { "future": {"other": "{0} روز بعد", "one": "{0} روز بعد"}, "past": {"other": "{0} روز پیش", "one": "{0} روز پیش"}, }, "hour": { "future": {"other": "{0} ساعت بعد", "one": "{0} ساعت بعد"}, "past": {"other": "{0} ساعت پیش", "one": "{0} ساعت پیش"}, }, "minute": { "future": {"other": "{0} دقیقه بعد", "one": "{0} دقیقه بعد"}, "past": {"other": "{0} دقیقه پیش", "one": "{0} دقیقه پیش"}, }, "second": { "future": {"other": "{0} ثانیه بعد", "one": "{0} ثانیه بعد"}, "past": {"other": "{0} ثانیه پیش", "one": "{0} ثانیه پیش"}, }, }, "day_periods": { "midnight": "نیمه\u200cشب", "am": "قبل\u200cازظهر", "noon": "ظهر", "pm": "بعدازظهر", "morning1": "صبح", "afternoon1": "عصر", "evening1": "عصر", "night1": "شب", }, "week_data": { "min_days": 1, "first_day": 0, "weekend_start": 5, "weekend_end": 6, }, }, "custom": custom_translations, } pendulum-3.0.0/src/pendulum/locales/fo/000077500000000000000000000000001453741033500200165ustar00rootroot00000000000000pendulum-3.0.0/src/pendulum/locales/fo/__init__.py000066400000000000000000000000001453741033500221150ustar00rootroot00000000000000pendulum-3.0.0/src/pendulum/locales/fo/custom.py000066400000000000000000000006751453741033500217120ustar00rootroot00000000000000""" fo custom locale file. """ from __future__ import annotations translations = { # Relative time "after": "{0} aftaná", "before": "{0} áðrenn", # Ordinals "ordinal": {"other": "."}, # Date formats "date_formats": { "LTS": "HH:mm:ss", "LT": "HH:mm", "LLLL": "dddd D. MMMM, YYYY HH:mm", "LLL": "D MMMM YYYY HH:mm", "LL": "D MMMM YYYY", "L": "DD/MM/YYYY", }, } pendulum-3.0.0/src/pendulum/locales/fo/locale.py000066400000000000000000000107171453741033500216350ustar00rootroot00000000000000from __future__ import annotations from pendulum.locales.fo.custom import translations as custom_translations """ fo locale file. It has been generated automatically and must not be modified directly. """ locale = { "plural": lambda n: "one" if (n == n and (n == 1)) else "other", "ordinal": lambda n: "other", "translations": { "days": { "abbreviated": { 0: "mán.", 1: "týs.", 2: "mik.", 3: "hós.", 4: "frí.", 5: "ley.", 6: "sun.", }, "narrow": {0: "M", 1: "T", 2: "M", 3: "H", 4: "F", 5: "L", 6: "S"}, "short": { 0: "má.", 1: "tý.", 2: "mi.", 3: "hó.", 4: "fr.", 5: "le.", 6: "su.", }, "wide": { 0: "mánadagur", 1: "týsdagur", 2: "mikudagur", 3: "hósdagur", 4: "fríggjadagur", 5: "leygardagur", 6: "sunnudagur", }, }, "months": { "abbreviated": { 1: "jan.", 2: "feb.", 3: "mar.", 4: "apr.", 5: "mai", 6: "jun.", 7: "jul.", 8: "aug.", 9: "sep.", 10: "okt.", 11: "nov.", 12: "des.", }, "narrow": { 1: "J", 2: "F", 3: "M", 4: "A", 5: "M", 6: "J", 7: "J", 8: "A", 9: "S", 10: "O", 11: "N", 12: "D", }, "wide": { 1: "januar", 2: "februar", 3: "mars", 4: "apríl", 5: "mai", 6: "juni", 7: "juli", 8: "august", 9: "september", 10: "oktober", 11: "november", 12: "desember", }, }, "units": { "year": {"one": "{0} ár", "other": "{0} ár"}, "month": {"one": "{0} mánaður", "other": "{0} mánaðir"}, "week": {"one": "{0} vika", "other": "{0} vikur"}, "day": {"one": "{0} dagur", "other": "{0} dagar"}, "hour": {"one": "{0} tími", "other": "{0} tímar"}, "minute": {"one": "{0} minuttur", "other": "{0} minuttir"}, "second": {"one": "{0} sekund", "other": "{0} sekundir"}, "microsecond": {"one": "{0} mikrosekund", "other": "{0} mikrosekundir"}, }, "relative": { "year": { "future": {"other": "um {0} ár", "one": "um {0} ár"}, "past": {"other": "{0} ár síðan", "one": "{0} ár síðan"}, }, "month": { "future": {"other": "um {0} mánaðir", "one": "um {0} mánað"}, "past": {"other": "{0} mánaðir síðan", "one": "{0} mánað síðan"}, }, "week": { "future": {"other": "um {0} vikur", "one": "um {0} viku"}, "past": {"other": "{0} vikur síðan", "one": "{0} vika síðan"}, }, "day": { "future": {"other": "um {0} dagar", "one": "um {0} dag"}, "past": {"other": "{0} dagar síðan", "one": "{0} dagur síðan"}, }, "hour": { "future": {"other": "um {0} tímar", "one": "um {0} tíma"}, "past": {"other": "{0} tímar síðan", "one": "{0} tími síðan"}, }, "minute": { "future": {"other": "um {0} minuttir", "one": "um {0} minutt"}, "past": {"other": "{0} minuttir síðan", "one": "{0} minutt síðan"}, }, "second": { "future": {"other": "um {0} sekund", "one": "um {0} sekund"}, "past": {"other": "{0} sekund síðan", "one": "{0} sekund síðan"}, }, }, "day_periods": {"am": "AM", "pm": "PM"}, "week_data": { "min_days": 1, "first_day": 0, "weekend_start": 5, "weekend_end": 6, }, }, "custom": custom_translations, } pendulum-3.0.0/src/pendulum/locales/fr/000077500000000000000000000000001453741033500200215ustar00rootroot00000000000000pendulum-3.0.0/src/pendulum/locales/fr/__init__.py000066400000000000000000000000001453741033500221200ustar00rootroot00000000000000pendulum-3.0.0/src/pendulum/locales/fr/custom.py000066400000000000000000000010531453741033500217040ustar00rootroot00000000000000""" fr custom locale file. """ from __future__ import annotations translations = { "units": {"few_second": "quelques secondes"}, # Relative Time "ago": "il y a {0}", "from_now": "dans {0}", "after": "{0} après", "before": "{0} avant", # Ordinals "ordinal": {"one": "er", "other": "e"}, # Date formats "date_formats": { "LTS": "HH:mm:ss", "LT": "HH:mm", "LLLL": "dddd D MMMM YYYY HH:mm", "LLL": "D MMMM YYYY HH:mm", "LL": "D MMMM YYYY", "L": "DD/MM/YYYY", }, } pendulum-3.0.0/src/pendulum/locales/fr/locale.py000066400000000000000000000111431453741033500216320ustar00rootroot00000000000000from __future__ import annotations from pendulum.locales.fr.custom import translations as custom_translations """ fr locale file. It has been generated automatically and must not be modified directly. """ locale = { "plural": lambda n: "one" if (n == n and ((n == 0) or (n == 1))) else "other", "ordinal": lambda n: "one" if (n == n and (n == 1)) else "other", "translations": { "days": { "abbreviated": { 0: "lun.", 1: "mar.", 2: "mer.", 3: "jeu.", 4: "ven.", 5: "sam.", 6: "dim.", }, "narrow": {0: "L", 1: "M", 2: "M", 3: "J", 4: "V", 5: "S", 6: "D"}, "short": {0: "lu", 1: "ma", 2: "me", 3: "je", 4: "ve", 5: "sa", 6: "di"}, "wide": { 0: "lundi", 1: "mardi", 2: "mercredi", 3: "jeudi", 4: "vendredi", 5: "samedi", 6: "dimanche", }, }, "months": { "abbreviated": { 1: "janv.", 2: "févr.", 3: "mars", 4: "avr.", 5: "mai", 6: "juin", 7: "juil.", 8: "août", 9: "sept.", 10: "oct.", 11: "nov.", 12: "déc.", }, "narrow": { 1: "J", 2: "F", 3: "M", 4: "A", 5: "M", 6: "J", 7: "J", 8: "A", 9: "S", 10: "O", 11: "N", 12: "D", }, "wide": { 1: "janvier", 2: "février", 3: "mars", 4: "avril", 5: "mai", 6: "juin", 7: "juillet", 8: "août", 9: "septembre", 10: "octobre", 11: "novembre", 12: "décembre", }, }, "units": { "year": {"one": "{0} an", "other": "{0} ans"}, "month": {"one": "{0} mois", "other": "{0} mois"}, "week": {"one": "{0} semaine", "other": "{0} semaines"}, "day": {"one": "{0} jour", "other": "{0} jours"}, "hour": {"one": "{0} heure", "other": "{0} heures"}, "minute": {"one": "{0} minute", "other": "{0} minutes"}, "second": {"one": "{0} seconde", "other": "{0} secondes"}, "microsecond": {"one": "{0} microseconde", "other": "{0} microsecondes"}, }, "relative": { "year": { "future": {"other": "dans {0} ans", "one": "dans {0} an"}, "past": {"other": "il y a {0} ans", "one": "il y a {0} an"}, }, "month": { "future": {"other": "dans {0} mois", "one": "dans {0} mois"}, "past": {"other": "il y a {0} mois", "one": "il y a {0} mois"}, }, "week": { "future": {"other": "dans {0} semaines", "one": "dans {0} semaine"}, "past": {"other": "il y a {0} semaines", "one": "il y a {0} semaine"}, }, "day": { "future": {"other": "dans {0} jours", "one": "dans {0} jour"}, "past": {"other": "il y a {0} jours", "one": "il y a {0} jour"}, }, "hour": { "future": {"other": "dans {0} heures", "one": "dans {0} heure"}, "past": {"other": "il y a {0} heures", "one": "il y a {0} heure"}, }, "minute": { "future": {"other": "dans {0} minutes", "one": "dans {0} minute"}, "past": {"other": "il y a {0} minutes", "one": "il y a {0} minute"}, }, "second": { "future": {"other": "dans {0} secondes", "one": "dans {0} seconde"}, "past": {"other": "il y a {0} secondes", "one": "il y a {0} seconde"}, }, }, "day_periods": { "midnight": "minuit", "am": "AM", "noon": "midi", "pm": "PM", "morning1": "du matin", "afternoon1": "de l’après-midi", "evening1": "du soir", "night1": "de nuit", }, "week_data": { "min_days": 1, "first_day": 0, "weekend_start": 5, "weekend_end": 6, }, }, "custom": custom_translations, } pendulum-3.0.0/src/pendulum/locales/he/000077500000000000000000000000001453741033500200065ustar00rootroot00000000000000pendulum-3.0.0/src/pendulum/locales/he/__init__.py000066400000000000000000000000001453741033500221050ustar00rootroot00000000000000pendulum-3.0.0/src/pendulum/locales/he/custom.py000066400000000000000000000011031453741033500216650ustar00rootroot00000000000000""" he custom locale file. """ from __future__ import annotations translations = { "units": {"few_second": "כמה שניות"}, # Relative time "ago": "לפני {0}", "from_now": "תוך {0}", "after": "בעוד {0}", "before": "{0} קוד×", # Ordinals "ordinal": {"other": "º"}, # Date formats "date_formats": { "LTS": "H:mm:ss", "LT": "H:mm", "LLLL": "dddd, D [ב] MMMM [ב] YYYY H:mm", "LLL": "D [ב] MMMM [ב] YYYY H:mm", "LL": "D [ב] MMMM [ב] YYYY", "L": "DD/MM/YYYY", }, } pendulum-3.0.0/src/pendulum/locales/he/locale.py000066400000000000000000000215601453741033500216230ustar00rootroot00000000000000from __future__ import annotations from pendulum.locales.he.custom import translations as custom_translations """ he locale file. It has been generated automatically and must not be modified directly. """ locale = { "plural": lambda n: "many" if ( ((0 == 0 and (0 == 0)) and (not (n == n and (n >= 0 and n <= 10)))) and ((n % 10) == (n % 10) and ((n % 10) == 0)) ) else "one" if ((n == n and (n == 1)) and (0 == 0 and (0 == 0))) else "two" if ((n == n and (n == 2)) and (0 == 0 and (0 == 0))) else "other", "ordinal": lambda n: "other", "translations": { "days": { "abbreviated": { 0: "×™×•× ×‘×³", 1: "×™×•× ×’×³", 2: "×™×•× ×“×³", 3: "×™×•× ×”×³", 4: "×™×•× ×•×³", 5: "שבת", 6: "×™×•× ×׳", }, "narrow": { 0: "ב׳", 1: "ג׳", 2: "ד׳", 3: "ה׳", 4: "ו׳", 5: "ש׳", 6: "×׳", }, "short": { 0: "ב׳", 1: "ג׳", 2: "ד׳", 3: "ה׳", 4: "ו׳", 5: "ש׳", 6: "×׳", }, "wide": { 0: "×™×•× ×©× ×™", 1: "×™×•× ×©×œ×™×©×™", 2: "×™×•× ×¨×‘×™×¢×™", 3: "×™×•× ×—×ž×™×©×™", 4: "×™×•× ×©×™×©×™", 5: "×™×•× ×©×‘×ª", 6: "×™×•× ×¨×שון", }, }, "months": { "abbreviated": { 1: "ינו׳", 2: "פבר׳", 3: "מרץ", 4: "×פר׳", 5: "מ××™", 6: "יוני", 7: "יולי", 8: "×וג׳", 9: "ספט׳", 10: "×וק׳", 11: "נוב׳", 12: "דצמ׳", }, "narrow": { 1: "1", 2: "2", 3: "3", 4: "4", 5: "5", 6: "6", 7: "7", 8: "8", 9: "9", 10: "10", 11: "11", 12: "12", }, "wide": { 1: "ינו×ר", 2: "פברו×ר", 3: "מרץ", 4: "×פריל", 5: "מ××™", 6: "יוני", 7: "יולי", 8: "×וגוסט", 9: "ספטמבר", 10: "×וקטובר", 11: "נובמבר", 12: "דצמבר", }, }, "units": { "year": { "one": "שנה", "two": "שנתיי×", "many": "{0} שני×", "other": "{0} שני×", }, "month": { "one": "חודש", "two": "חודשיי×", "many": "{0} חודשי×", "other": "{0} חודשי×", }, "week": { "one": "שבוע", "two": "שבועיי×", "many": "{0} שבועות", "other": "{0} שבועות", }, "day": { "one": "×™×•× {0}", "two": "יומיי×", "many": "{0} יו×", "other": "{0} ימי×", }, "hour": { "one": "שעה", "two": "שעתיי×", "many": "{0} שעות", "other": "{0} שעות", }, "minute": { "one": "דקה", "two": "שתי דקות", "many": "{0} דקות", "other": "{0} דקות", }, "second": { "one": "שניה", "two": "שתי שניות", "many": "\u200f{0} שניות", "other": "{0} שניות", }, "microsecond": { "one": "{0} מיליונית שנייה", "two": "{0} מיליוניות שנייה", "many": "{0} מיליוניות שנייה", "other": "{0} מיליוניות שנייה", }, }, "relative": { "year": { "future": { "other": "בעוד {0} שני×", "one": "בעוד שנה", "two": "בעוד שנתיי×", "many": "בעוד {0} שנה", }, "past": { "other": "לפני {0} שני×", "one": "לפני שנה", "two": "לפני שנתיי×", "many": "לפני {0} שנה", }, }, "month": { "future": { "other": "בעוד {0} חודשי×", "one": "בעוד חודש", "two": "בעוד חודשיי×", "many": "בעוד {0} חודשי×", }, "past": { "other": "לפני {0} חודשי×", "one": "לפני חודש", "two": "לפני חודשיי×", "many": "לפני {0} חודשי×", }, }, "week": { "future": { "other": "בעוד {0} שבועות", "one": "בעוד שבוע", "two": "בעוד שבועיי×", "many": "בעוד {0} שבועות", }, "past": { "other": "לפני {0} שבועות", "one": "לפני שבוע", "two": "לפני שבועיי×", "many": "לפני {0} שבועות", }, }, "day": { "future": { "other": "בעוד {0} ימי×", "one": "בעוד ×™×•× {0}", "two": "בעוד יומיי×", "many": "בעוד {0} ימי×", }, "past": { "other": "לפני {0} ימי×", "one": "לפני ×™×•× {0}", "two": "לפני יומיי×", "many": "לפני {0} ימי×", }, }, "hour": { "future": { "other": "בעוד {0} שעות", "one": "בעוד שעה", "two": "בעוד שעתיי×", "many": "בעוד {0} שעות", }, "past": { "other": "לפני {0} שעות", "one": "לפני שעה", "two": "לפני שעתיי×", "many": "לפני {0} שעות", }, }, "minute": { "future": { "other": "בעוד {0} דקות", "one": "בעוד דקה", "two": "בעוד שתי דקות", "many": "בעוד {0} דקות", }, "past": { "other": "לפני {0} דקות", "one": "לפני דקה", "two": "לפני שתי דקות", "many": "לפני {0} דקות", }, }, "second": { "future": { "other": "בעוד {0} שניות", "one": "בעוד שנייה", "two": "בעוד שתי שניות", "many": "בעוד {0} שניות", }, "past": { "other": "לפני {0} שניות", "one": "לפני שנייה", "two": "לפני שתי שניות", "many": "לפני {0} שניות", }, }, }, "day_periods": { "midnight": "חצות", "am": "לפנה״צ", "pm": "×חה״צ", "morning1": "בבוקר", "afternoon1": "בצהריי×", "afternoon2": "×חר הצהריי×", "evening1": "בערב", "night1": "בלילה", "night2": "לפנות בוקר", }, "week_data": { "min_days": 1, "first_day": 0, "weekend_start": 5, "weekend_end": 6, }, }, "custom": custom_translations, } pendulum-3.0.0/src/pendulum/locales/id/000077500000000000000000000000001453741033500200065ustar00rootroot00000000000000pendulum-3.0.0/src/pendulum/locales/id/__init__.py000066400000000000000000000000001453741033500221050ustar00rootroot00000000000000pendulum-3.0.0/src/pendulum/locales/id/custom.py000066400000000000000000000007261453741033500216770ustar00rootroot00000000000000""" id custom locale file. """ from __future__ import annotations translations = { "units": {"few_second": "beberapa detik"}, "ago": "{} yang lalu", "from_now": "dalam {}", "after": "{0} kemudian", "before": "{0} yang lalu", "date_formats": { "LTS": "HH:mm:ss", "LT": "HH:mm", "LLLL": "dddd [d.] D. MMMM YYYY HH:mm", "LLL": "D. MMMM YYYY HH:mm", "LL": "D. MMMM YYYY", "L": "DD/MM/YYYY", }, } pendulum-3.0.0/src/pendulum/locales/id/locale.py000066400000000000000000000101251453741033500216160ustar00rootroot00000000000000from __future__ import annotations from pendulum.locales.id.custom import translations as custom_translations """ id locale file. It has been generated automatically and must not be modified directly. """ locale = { "plural": lambda n: "other", "ordinal": lambda n: "other", "translations": { "days": { "abbreviated": { 0: "Sen", 1: "Sel", 2: "Rab", 3: "Kam", 4: "Jum", 5: "Sab", 6: "Min", }, "narrow": {0: "S", 1: "S", 2: "R", 3: "K", 4: "J", 5: "S", 6: "M"}, "short": { 0: "Sen", 1: "Sel", 2: "Rab", 3: "Kam", 4: "Jum", 5: "Sab", 6: "Min", }, "wide": { 0: "Senin", 1: "Selasa", 2: "Rabu", 3: "Kamis", 4: "Jumat", 5: "Sabtu", 6: "Minggu", }, }, "months": { "abbreviated": { 1: "Jan", 2: "Feb", 3: "Mar", 4: "Apr", 5: "Mei", 6: "Jun", 7: "Jul", 8: "Agt", 9: "Sep", 10: "Okt", 11: "Nov", 12: "Des", }, "narrow": { 1: "J", 2: "F", 3: "M", 4: "A", 5: "M", 6: "J", 7: "J", 8: "A", 9: "S", 10: "O", 11: "N", 12: "D", }, "wide": { 1: "Januari", 2: "Februari", 3: "Maret", 4: "April", 5: "Mei", 6: "Juni", 7: "Juli", 8: "Agustus", 9: "September", 10: "Oktober", 11: "November", 12: "Desember", }, }, "units": { "year": {"other": "{0} tahun"}, "month": {"other": "{0} bulan"}, "week": {"other": "{0} minggu"}, "day": {"other": "{0} hari"}, "hour": {"other": "{0} jam"}, "minute": {"other": "{0} menit"}, "second": {"other": "{0} detik"}, "microsecond": {"other": "{0} mikrodetik"}, }, "relative": { "year": { "future": {"other": "dalam {0} tahun"}, "past": {"other": "{0} tahun yang lalu"}, }, "month": { "future": {"other": "dalam {0} bulan"}, "past": {"other": "{0} bulan yang lalu"}, }, "week": { "future": {"other": "dalam {0} minggu"}, "past": {"other": "{0} minggu yang lalu"}, }, "day": { "future": {"other": "dalam {0} hari"}, "past": {"other": "{0} hari yang lalu"}, }, "hour": { "future": {"other": "dalam {0} jam"}, "past": {"other": "{0} jam yang lalu"}, }, "minute": { "future": {"other": "dalam {0} menit"}, "past": {"other": "{0} menit yang lalu"}, }, "second": { "future": {"other": "dalam {0} detik"}, "past": {"other": "{0} detik yang lalu"}, }, }, "day_periods": { "midnight": "tengah malam", "am": "AM", "noon": "tengah hari", "pm": "PM", "morning1": "pagi", "afternoon1": "siang", "evening1": "sore", "night1": "malam", }, "week_data": { "min_days": 1, "first_day": 0, "weekend_start": 5, "weekend_end": 6, }, }, "custom": custom_translations, } pendulum-3.0.0/src/pendulum/locales/it/000077500000000000000000000000001453741033500200265ustar00rootroot00000000000000pendulum-3.0.0/src/pendulum/locales/it/__init__.py000066400000000000000000000000001453741033500221250ustar00rootroot00000000000000pendulum-3.0.0/src/pendulum/locales/it/custom.py000066400000000000000000000010371453741033500217130ustar00rootroot00000000000000""" it custom locale file. """ from __future__ import annotations translations = { "units": {"few_second": "alcuni secondi"}, # Relative Time "ago": "{0} fa", "from_now": "in {0}", "after": "{0} dopo", "before": "{0} prima", # Ordinals "ordinal": {"other": "°"}, # Date formats "date_formats": { "LTS": "H:mm:ss", "LT": "H:mm", "L": "DD/MM/YYYY", "LL": "D MMMM YYYY", "LLL": "D MMMM YYYY [alle] H:mm", "LLLL": "dddd, D MMMM YYYY [alle] H:mm", }, } pendulum-3.0.0/src/pendulum/locales/it/locale.py000066400000000000000000000113621453741033500216420ustar00rootroot00000000000000from __future__ import annotations from pendulum.locales.it.custom import translations as custom_translations """ it locale file. It has been generated automatically and must not be modified directly. """ locale = { "plural": lambda n: "one" if ((n == n and (n == 1)) and (0 == 0 and (0 == 0))) else "other", "ordinal": lambda n: "many" if (n == n and ((n == 11) or (n == 8) or (n == 80) or (n == 800))) else "other", "translations": { "days": { "abbreviated": { 0: "lun", 1: "mar", 2: "mer", 3: "gio", 4: "ven", 5: "sab", 6: "dom", }, "narrow": {0: "L", 1: "M", 2: "M", 3: "G", 4: "V", 5: "S", 6: "D"}, "short": { 0: "lun", 1: "mar", 2: "mer", 3: "gio", 4: "ven", 5: "sab", 6: "dom", }, "wide": { 0: "lunedì", 1: "martedì", 2: "mercoledì", 3: "giovedì", 4: "venerdì", 5: "sabato", 6: "domenica", }, }, "months": { "abbreviated": { 1: "gen", 2: "feb", 3: "mar", 4: "apr", 5: "mag", 6: "giu", 7: "lug", 8: "ago", 9: "set", 10: "ott", 11: "nov", 12: "dic", }, "narrow": { 1: "G", 2: "F", 3: "M", 4: "A", 5: "M", 6: "G", 7: "L", 8: "A", 9: "S", 10: "O", 11: "N", 12: "D", }, "wide": { 1: "gennaio", 2: "febbraio", 3: "marzo", 4: "aprile", 5: "maggio", 6: "giugno", 7: "luglio", 8: "agosto", 9: "settembre", 10: "ottobre", 11: "novembre", 12: "dicembre", }, }, "units": { "year": {"one": "{0} anno", "other": "{0} anni"}, "month": {"one": "{0} mese", "other": "{0} mesi"}, "week": {"one": "{0} settimana", "other": "{0} settimane"}, "day": {"one": "{0} giorno", "other": "{0} giorni"}, "hour": {"one": "{0} ora", "other": "{0} ore"}, "minute": {"one": "{0} minuto", "other": "{0} minuti"}, "second": {"one": "{0} secondo", "other": "{0} secondi"}, "microsecond": {"one": "{0} microsecondo", "other": "{0} microsecondi"}, }, "relative": { "year": { "future": {"other": "tra {0} anni", "one": "tra {0} anno"}, "past": {"other": "{0} anni fa", "one": "{0} anno fa"}, }, "month": { "future": {"other": "tra {0} mesi", "one": "tra {0} mese"}, "past": {"other": "{0} mesi fa", "one": "{0} mese fa"}, }, "week": { "future": {"other": "tra {0} settimane", "one": "tra {0} settimana"}, "past": {"other": "{0} settimane fa", "one": "{0} settimana fa"}, }, "day": { "future": {"other": "tra {0} giorni", "one": "tra {0} giorno"}, "past": {"other": "{0} giorni fa", "one": "{0} giorno fa"}, }, "hour": { "future": {"other": "tra {0} ore", "one": "tra {0} ora"}, "past": {"other": "{0} ore fa", "one": "{0} ora fa"}, }, "minute": { "future": {"other": "tra {0} minuti", "one": "tra {0} minuto"}, "past": {"other": "{0} minuti fa", "one": "{0} minuto fa"}, }, "second": { "future": {"other": "tra {0} secondi", "one": "tra {0} secondo"}, "past": {"other": "{0} secondi fa", "one": "{0} secondo fa"}, }, }, "day_periods": { "midnight": "mezzanotte", "am": "AM", "noon": "mezzogiorno", "pm": "PM", "morning1": "di mattina", "afternoon1": "del pomeriggio", "evening1": "di sera", "night1": "di notte", }, "week_data": { "min_days": 1, "first_day": 0, "weekend_start": 5, "weekend_end": 6, }, }, "custom": custom_translations, } pendulum-3.0.0/src/pendulum/locales/ja/000077500000000000000000000000001453741033500200045ustar00rootroot00000000000000pendulum-3.0.0/src/pendulum/locales/ja/__init__.py000066400000000000000000000000001453741033500221030ustar00rootroot00000000000000pendulum-3.0.0/src/pendulum/locales/ja/custom.py000066400000000000000000000007531453741033500216750ustar00rootroot00000000000000""" ja custom locale file. """ from __future__ import annotations translations = { "units": {"few_second": "æ•°ç§’"}, # Relative time "ago": "{} å‰ã«", "from_now": "今ã‹ã‚‰ {}", "after": "{0} 後", "before": "{0} å‰", # Date formats "date_formats": { "LTS": "h:mm:ss A", "LT": "h:mm A", "L": "MM/DD/YYYY", "LL": "MMMM D, YYYY", "LLL": "MMMM D, YYYY h:mm A", "LLLL": "dddd, MMMM D, YYYY h:mm A", }, } pendulum-3.0.0/src/pendulum/locales/ja/locale.py000066400000000000000000000117441453741033500216240ustar00rootroot00000000000000from __future__ import annotations from pendulum.locales.ja.custom import translations as custom_translations """ ja locale file. It has been generated automatically and must not be modified directly. """ locale = { "plural": lambda n: "other", "ordinal": lambda n: "other", "translations": { "days": { "abbreviated": { 0: "月", 1: "ç«", 2: "æ°´", 3: "木", 4: "金", 5: "土", 6: "æ—¥", }, "narrow": { 0: "月", 1: "ç«", 2: "æ°´", 3: "木", 4: "金", 5: "土", 6: "æ—¥", }, "short": { 0: "月", 1: "ç«", 2: "æ°´", 3: "木", 4: "金", 5: "土", 6: "æ—¥", }, "wide": { 0: "月曜日", 1: "ç«æ›œæ—¥", 2: "水曜日", 3: "木曜日", 4: "金曜日", 5: "土曜日", 6: "日曜日", }, }, "months": { "abbreviated": { 1: "1月", 2: "2月", 3: "3月", 4: "4月", 5: "5月", 6: "6月", 7: "7月", 8: "8月", 9: "9月", 10: "10月", 11: "11月", 12: "12月", }, "narrow": { 1: "1", 2: "2", 3: "3", 4: "4", 5: "5", 6: "6", 7: "7", 8: "8", 9: "9", 10: "10", 11: "11", 12: "12", }, "wide": { 1: "1月", 2: "2月", 3: "3月", 4: "4月", 5: "5月", 6: "6月", 7: "7月", 8: "8月", 9: "9月", 10: "10月", 11: "11月", 12: "12月", }, }, "units": { "year": { "other": "{0} å¹´", }, "month": { "other": "{0} ã‹æœˆ", }, "week": { "other": "{0} 週間", }, "day": { "other": "{0} æ—¥", }, "hour": { "other": "{0} 時間", }, "minute": { "other": "{0} 分", }, "second": { "other": "{0} ç§’", }, "microsecond": { "other": "{0} マイクロ秒", }, }, "relative": { "year": { "future": { "other": "{0} 年後", }, "past": { "other": "{0} å¹´å‰", }, }, "month": { "future": { "other": "{0} ã‹æœˆå¾Œ", }, "past": { "other": "{0} ã‹æœˆå‰", }, }, "week": { "future": { "other": "{0} 週間後", }, "past": { "other": "{0} 週間å‰", }, }, "day": { "future": { "other": "{0} 日後", }, "past": { "other": "{0} æ—¥å‰", }, }, "hour": { "future": { "other": "{0} 時間後", }, "past": { "other": "{0} 時間å‰", }, }, "minute": { "future": { "other": "{0} 分後", }, "past": { "other": "{0} 分å‰", }, }, "second": { "future": { "other": "{0} 秒後", }, "past": { "other": "{0} ç§’å‰", }, }, }, "day_periods": { "midnight": "真夜中", "am": "åˆå‰", "noon": "æ­£åˆ", "pm": "åˆå¾Œ", "morning1": "æœ", "afternoon1": "昼", "evening1": "夕方", "night1": "夜", "night2": "夜中", }, "week_data": { "min_days": 1, "first_day": 0, "weekend_start": 5, "weekend_end": 6, }, }, "custom": custom_translations, } pendulum-3.0.0/src/pendulum/locales/ko/000077500000000000000000000000001453741033500200235ustar00rootroot00000000000000pendulum-3.0.0/src/pendulum/locales/ko/__init__.py000066400000000000000000000000001453741033500221220ustar00rootroot00000000000000pendulum-3.0.0/src/pendulum/locales/ko/custom.py000066400000000000000000000006601453741033500217110ustar00rootroot00000000000000""" ko custom locale file. """ from __future__ import annotations translations = { # Relative time "after": "{0} ë’¤", "before": "{0} 앞", # Date formats "date_formats": { "LTS": "A h시 më¶„ sì´ˆ", "LT": "A h시 më¶„", "LLLL": "YYYYë…„ MMMM Dì¼ dddd A h시 më¶„", "LLL": "YYYYë…„ MMMM Dì¼ A h시 më¶„", "LL": "YYYYë…„ MMMM Dì¼", "L": "YYYY.MM.DD", }, } pendulum-3.0.0/src/pendulum/locales/ko/locale.py000066400000000000000000000070341453741033500216400ustar00rootroot00000000000000from __future__ import annotations from pendulum.locales.ko.custom import translations as custom_translations """ ko locale file. It has been generated automatically and must not be modified directly. """ locale = { "plural": lambda n: "other", "ordinal": lambda n: "other", "translations": { "days": { "abbreviated": {0: "ì›”", 1: "í™”", 2: "수", 3: "목", 4: "금", 5: "토", 6: "ì¼"}, "narrow": {0: "ì›”", 1: "í™”", 2: "수", 3: "목", 4: "금", 5: "토", 6: "ì¼"}, "short": {0: "ì›”", 1: "í™”", 2: "수", 3: "목", 4: "금", 5: "토", 6: "ì¼"}, "wide": { 0: "월요ì¼", 1: "화요ì¼", 2: "수요ì¼", 3: "목요ì¼", 4: "금요ì¼", 5: "토요ì¼", 6: "ì¼ìš”ì¼", }, }, "months": { "abbreviated": { 1: "1ì›”", 2: "2ì›”", 3: "3ì›”", 4: "4ì›”", 5: "5ì›”", 6: "6ì›”", 7: "7ì›”", 8: "8ì›”", 9: "9ì›”", 10: "10ì›”", 11: "11ì›”", 12: "12ì›”", }, "narrow": { 1: "1ì›”", 2: "2ì›”", 3: "3ì›”", 4: "4ì›”", 5: "5ì›”", 6: "6ì›”", 7: "7ì›”", 8: "8ì›”", 9: "9ì›”", 10: "10ì›”", 11: "11ì›”", 12: "12ì›”", }, "wide": { 1: "1ì›”", 2: "2ì›”", 3: "3ì›”", 4: "4ì›”", 5: "5ì›”", 6: "6ì›”", 7: "7ì›”", 8: "8ì›”", 9: "9ì›”", 10: "10ì›”", 11: "11ì›”", 12: "12ì›”", }, }, "units": { "year": {"other": "{0}ë…„"}, "month": {"other": "{0}개월"}, "week": {"other": "{0}주"}, "day": {"other": "{0}ì¼"}, "hour": {"other": "{0}시간"}, "minute": {"other": "{0}ë¶„"}, "second": {"other": "{0}ì´ˆ"}, "microsecond": {"other": "{0}마ì´í¬ë¡œì´ˆ"}, }, "relative": { "year": {"future": {"other": "{0}ë…„ 후"}, "past": {"other": "{0}ë…„ ì „"}}, "month": {"future": {"other": "{0}개월 후"}, "past": {"other": "{0}개월 ì „"}}, "week": {"future": {"other": "{0}주 후"}, "past": {"other": "{0}주 ì „"}}, "day": {"future": {"other": "{0}ì¼ í›„"}, "past": {"other": "{0}ì¼ ì „"}}, "hour": {"future": {"other": "{0}시간 후"}, "past": {"other": "{0}시간 ì „"}}, "minute": {"future": {"other": "{0}ë¶„ 후"}, "past": {"other": "{0}ë¶„ ì „"}}, "second": {"future": {"other": "{0}ì´ˆ 후"}, "past": {"other": "{0}ì´ˆ ì „"}}, }, "day_periods": { "midnight": "ìžì •", "am": "오전", "noon": "정오", "pm": "오후", "morning1": "새벽", "morning2": "오전", "afternoon1": "오후", "evening1": "ì €ë…", "night1": "ë°¤", }, "week_data": { "min_days": 1, "first_day": 0, "weekend_start": 5, "weekend_end": 6, }, }, "custom": custom_translations, } pendulum-3.0.0/src/pendulum/locales/locale.py000066400000000000000000000054641453741033500212340ustar00rootroot00000000000000from __future__ import annotations import re from importlib import import_module from pathlib import Path from typing import Any from typing import ClassVar from typing import Dict from typing import cast from pendulum.utils._compat import resources class Locale: """ Represent a specific locale. """ _cache: ClassVar[dict[str, Locale]] = {} def __init__(self, locale: str, data: Any) -> None: self._locale: str = locale self._data: Any = data self._key_cache: dict[str, str] = {} @classmethod def load(cls, locale: str | Locale) -> Locale: if isinstance(locale, Locale): return locale locale = cls.normalize_locale(locale) if locale in cls._cache: return cls._cache[locale] # Checking locale existence actual_locale = locale locale_path = cast(Path, resources.files(__package__).joinpath(actual_locale)) while not locale_path.exists(): if actual_locale == locale: raise ValueError(f"Locale [{locale}] does not exist.") actual_locale = actual_locale.split("_")[0] m = import_module(f"pendulum.locales.{actual_locale}.locale") cls._cache[locale] = cls(locale, m.locale) return cls._cache[locale] @classmethod def normalize_locale(cls, locale: str) -> str: m = re.match("([a-z]{2})[-_]([a-z]{2})", locale, re.I) if m: return f"{m.group(1).lower()}_{m.group(2).lower()}" else: return locale.lower() def get(self, key: str, default: Any | None = None) -> Any: if key in self._key_cache: return self._key_cache[key] parts = key.split(".") try: result = self._data[parts[0]] for part in parts[1:]: result = result[part] except KeyError: result = default self._key_cache[key] = result return self._key_cache[key] def translation(self, key: str) -> Any: return self.get(f"translations.{key}") def plural(self, number: int) -> str: return cast(str, self._data["plural"](number)) def ordinal(self, number: int) -> str: return cast(str, self._data["ordinal"](number)) def ordinalize(self, number: int) -> str: ordinal = self.get(f"custom.ordinal.{self.ordinal(number)}") if not ordinal: return f"{number}" return f"{number}{ordinal}" def match_translation(self, key: str, value: Any) -> dict[str, str] | None: translations = self.translation(key) if value not in translations.values(): return None return cast(Dict[str, str], {v: k for k, v in translations.items()}[value]) def __repr__(self) -> str: return f"{self.__class__.__name__}('{self._locale}')" pendulum-3.0.0/src/pendulum/locales/lt/000077500000000000000000000000001453741033500200315ustar00rootroot00000000000000pendulum-3.0.0/src/pendulum/locales/lt/__init__.py000066400000000000000000000000001453741033500221300ustar00rootroot00000000000000pendulum-3.0.0/src/pendulum/locales/lt/custom.py000066400000000000000000000065361453741033500217270ustar00rootroot00000000000000""" lt custom locale file. """ from __future__ import annotations translations = { # Relative time "units_relative": { "year": { "future": { "other": "{0} metų", "one": "{0} metų", "few": "{0} metų", "many": "{0} metų", }, "past": { "other": "{0} metų", "one": "{0} metus", "few": "{0} metus", "many": "{0} metų", }, }, "month": { "future": { "other": "{0} mÄ—nesių", "one": "{0} mÄ—nesio", "few": "{0} mÄ—nesių", "many": "{0} mÄ—nesio", }, "past": { "other": "{0} mÄ—nesių", "one": "{0} mÄ—nesį", "few": "{0} mÄ—nesius", "many": "{0} mÄ—nesio", }, }, "week": { "future": { "other": "{0} savaiÄių", "one": "{0} savaitÄ—s", "few": "{0} savaiÄių", "many": "{0} savaitÄ—s", }, "past": { "other": "{0} savaiÄių", "one": "{0} savaitÄ™", "few": "{0} savaites", "many": "{0} savaitÄ—s", }, }, "day": { "future": { "other": "{0} dienų", "one": "{0} dienos", "few": "{0} dienų", "many": "{0} dienos", }, "past": { "other": "{0} dienų", "one": "{0} dienÄ…", "few": "{0} dienas", "many": "{0} dienos", }, }, "hour": { "future": { "other": "{0} valandų", "one": "{0} valandos", "few": "{0} valandų", "many": "{0} valandos", }, "past": { "other": "{0} valandų", "one": "{0} valandÄ…", "few": "{0} valandas", "many": "{0} valandos", }, }, "minute": { "future": { "other": "{0} minuÄių", "one": "{0} minutÄ—s", "few": "{0} minuÄių", "many": "{0} minutÄ—s", }, "past": { "other": "{0} minuÄių", "one": "{0} minutÄ™", "few": "{0} minutes", "many": "{0} minutÄ—s", }, }, "second": { "future": { "other": "{0} sekundžių", "one": "{0} sekundÄ—s", "few": "{0} sekundžių", "many": "{0} sekundÄ—s", }, "past": { "other": "{0} sekundžių", "one": "{0} sekundÄ™", "few": "{0} sekundes", "many": "{0} sekundÄ—s", }, }, }, "after": "po {0}", "before": "{0} nuo dabar", # Date formats "date_formats": { "LTS": "HH:mm:ss", "LT": "HH:mm", "LLLL": "YYYY [m.] MMMM D [d.], dddd, HH:mm [val.]", "LLL": "YYYY [m.] MMMM D [d.], HH:mm [val.]", "LL": "YYYY [m.] MMMM D [d.]", "L": "YYYY-MM-DD", }, } pendulum-3.0.0/src/pendulum/locales/lt/locale.py000066400000000000000000000202261453741033500216440ustar00rootroot00000000000000from __future__ import annotations from pendulum.locales.lt.custom import translations as custom_translations """ lt locale file. It has been generated automatically and must not be modified directly. """ locale = { "plural": lambda n: "few" if ( ((n % 10) == (n % 10) and ((n % 10) >= 2 and (n % 10) <= 9)) and (not ((n % 100) == (n % 100) and ((n % 100) >= 11 and (n % 100) <= 19))) ) else "many" if (not (0 == 0 and (0 == 0))) else "one" if ( ((n % 10) == (n % 10) and ((n % 10) == 1)) and (not ((n % 100) == (n % 100) and ((n % 100) >= 11 and (n % 100) <= 19))) ) else "other", "ordinal": lambda n: "other", "translations": { "days": { "abbreviated": { 0: "pr", 1: "an", 2: "tr", 3: "kt", 4: "pn", 5: "Å¡t", 6: "sk", }, "narrow": {0: "P", 1: "A", 2: "T", 3: "K", 4: "P", 5: "Å ", 6: "S"}, "short": {0: "Pr", 1: "An", 2: "Tr", 3: "Kt", 4: "Pn", 5: "Å t", 6: "Sk"}, "wide": { 0: "pirmadienis", 1: "antradienis", 2: "treÄiadienis", 3: "ketvirtadienis", 4: "penktadienis", 5: "Å¡eÅ¡tadienis", 6: "sekmadienis", }, }, "months": { "abbreviated": { 1: "saus.", 2: "vas.", 3: "kov.", 4: "bal.", 5: "geg.", 6: "birž.", 7: "liep.", 8: "rugp.", 9: "rugs.", 10: "spal.", 11: "lapkr.", 12: "gruod.", }, "narrow": { 1: "S", 2: "V", 3: "K", 4: "B", 5: "G", 6: "B", 7: "L", 8: "R", 9: "R", 10: "S", 11: "L", 12: "G", }, "wide": { 1: "sausio", 2: "vasario", 3: "kovo", 4: "balandžio", 5: "gegužės", 6: "birželio", 7: "liepos", 8: "rugpjÅ«Äio", 9: "rugsÄ—jo", 10: "spalio", 11: "lapkriÄio", 12: "gruodžio", }, }, "units": { "year": { "one": "{0} metai", "few": "{0} metai", "many": "{0} metų", "other": "{0} metų", }, "month": { "one": "{0} mÄ—nuo", "few": "{0} mÄ—nesiai", "many": "{0} mÄ—nesio", "other": "{0} mÄ—nesių", }, "week": { "one": "{0} savaitÄ—", "few": "{0} savaitÄ—s", "many": "{0} savaitÄ—s", "other": "{0} savaiÄių", }, "day": { "one": "{0} diena", "few": "{0} dienos", "many": "{0} dienos", "other": "{0} dienų", }, "hour": { "one": "{0} valanda", "few": "{0} valandos", "many": "{0} valandos", "other": "{0} valandų", }, "minute": { "one": "{0} minutÄ—", "few": "{0} minutÄ—s", "many": "{0} minutÄ—s", "other": "{0} minuÄių", }, "second": { "one": "{0} sekundÄ—", "few": "{0} sekundÄ—s", "many": "{0} sekundÄ—s", "other": "{0} sekundžių", }, "microsecond": { "one": "{0} mikrosekundÄ—", "few": "{0} mikrosekundÄ—s", "many": "{0} mikrosekundÄ—s", "other": "{0} mikrosekundžių", }, }, "relative": { "year": { "future": { "other": "po {0} metų", "one": "po {0} metų", "few": "po {0} metų", "many": "po {0} metų", }, "past": { "other": "prieÅ¡ {0} metų", "one": "prieÅ¡ {0} metus", "few": "prieÅ¡ {0} metus", "many": "prieÅ¡ {0} metų", }, }, "month": { "future": { "other": "po {0} mÄ—nesių", "one": "po {0} mÄ—nesio", "few": "po {0} mÄ—nesių", "many": "po {0} mÄ—nesio", }, "past": { "other": "prieÅ¡ {0} mÄ—nesių", "one": "prieÅ¡ {0} mÄ—nesį", "few": "prieÅ¡ {0} mÄ—nesius", "many": "prieÅ¡ {0} mÄ—nesio", }, }, "week": { "future": { "other": "po {0} savaiÄių", "one": "po {0} savaitÄ—s", "few": "po {0} savaiÄių", "many": "po {0} savaitÄ—s", }, "past": { "other": "prieÅ¡ {0} savaiÄių", "one": "prieÅ¡ {0} savaitÄ™", "few": "prieÅ¡ {0} savaites", "many": "prieÅ¡ {0} savaitÄ—s", }, }, "day": { "future": { "other": "po {0} dienų", "one": "po {0} dienos", "few": "po {0} dienų", "many": "po {0} dienos", }, "past": { "other": "prieÅ¡ {0} dienų", "one": "prieÅ¡ {0} dienÄ…", "few": "prieÅ¡ {0} dienas", "many": "prieÅ¡ {0} dienos", }, }, "hour": { "future": { "other": "po {0} valandų", "one": "po {0} valandos", "few": "po {0} valandų", "many": "po {0} valandos", }, "past": { "other": "prieÅ¡ {0} valandų", "one": "prieÅ¡ {0} valandÄ…", "few": "prieÅ¡ {0} valandas", "many": "prieÅ¡ {0} valandos", }, }, "minute": { "future": { "other": "po {0} minuÄių", "one": "po {0} minutÄ—s", "few": "po {0} minuÄių", "many": "po {0} minutÄ—s", }, "past": { "other": "prieÅ¡ {0} minuÄių", "one": "prieÅ¡ {0} minutÄ™", "few": "prieÅ¡ {0} minutes", "many": "prieÅ¡ {0} minutÄ—s", }, }, "second": { "future": { "other": "po {0} sekundžių", "one": "po {0} sekundÄ—s", "few": "po {0} sekundžių", "many": "po {0} sekundÄ—s", }, "past": { "other": "prieÅ¡ {0} sekundžių", "one": "prieÅ¡ {0} sekundÄ™", "few": "prieÅ¡ {0} sekundes", "many": "prieÅ¡ {0} sekundÄ—s", }, }, }, "day_periods": { "midnight": "vidurnaktis", "am": "prieÅ¡piet", "noon": "perpiet", "pm": "popiet", "morning1": "rytas", "afternoon1": "popietÄ—", "evening1": "vakaras", "night1": "naktis", }, "week_data": { "min_days": 1, "first_day": 0, "weekend_start": 5, "weekend_end": 6, }, }, "custom": custom_translations, } pendulum-3.0.0/src/pendulum/locales/nb/000077500000000000000000000000001453741033500200115ustar00rootroot00000000000000pendulum-3.0.0/src/pendulum/locales/nb/__init__.py000066400000000000000000000000001453741033500221100ustar00rootroot00000000000000pendulum-3.0.0/src/pendulum/locales/nb/custom.py000066400000000000000000000007341453741033500217010ustar00rootroot00000000000000""" nn custom locale file. """ from __future__ import annotations translations = { # Relative time "after": "{0} etter", "before": "{0} før", # Ordinals "ordinal": {"one": ".", "two": ".", "few": ".", "other": "."}, # Date formats "date_formats": { "LTS": "HH:mm:ss", "LT": "HH:mm", "LLLL": "dddd Do MMMM YYYY HH:mm", "LLL": "Do MMMM YYYY HH:mm", "LL": "Do MMMM YYYY", "L": "DD.MM.YYYY", }, } pendulum-3.0.0/src/pendulum/locales/nb/locale.py000066400000000000000000000115541453741033500216300ustar00rootroot00000000000000from __future__ import annotations from pendulum.locales.nb.custom import translations as custom_translations """ nb locale file. It has been generated automatically and must not be modified directly. """ locale = { "plural": lambda n: "one" if (n == n and (n == 1)) else "other", "ordinal": lambda n: "other", "translations": { "days": { "abbreviated": { 0: "man.", 1: "tir.", 2: "ons.", 3: "tor.", 4: "fre.", 5: "lør.", 6: "søn.", }, "narrow": {0: "M", 1: "T", 2: "O", 3: "T", 4: "F", 5: "L", 6: "S"}, "short": { 0: "ma.", 1: "ti.", 2: "on.", 3: "to.", 4: "fr.", 5: "lø.", 6: "sø.", }, "wide": { 0: "mandag", 1: "tirsdag", 2: "onsdag", 3: "torsdag", 4: "fredag", 5: "lørdag", 6: "søndag", }, }, "months": { "abbreviated": { 1: "jan.", 2: "feb.", 3: "mar.", 4: "apr.", 5: "mai", 6: "jun.", 7: "jul.", 8: "aug.", 9: "sep.", 10: "okt.", 11: "nov.", 12: "des.", }, "narrow": { 1: "J", 2: "F", 3: "M", 4: "A", 5: "M", 6: "J", 7: "J", 8: "A", 9: "S", 10: "O", 11: "N", 12: "D", }, "wide": { 1: "januar", 2: "februar", 3: "mars", 4: "april", 5: "mai", 6: "juni", 7: "juli", 8: "august", 9: "september", 10: "oktober", 11: "november", 12: "desember", }, }, "units": { "year": {"one": "{0} Ã¥r", "other": "{0} Ã¥r"}, "month": {"one": "{0} mÃ¥ned", "other": "{0} mÃ¥neder"}, "week": {"one": "{0} uke", "other": "{0} uker"}, "day": {"one": "{0} dag", "other": "{0} dager"}, "hour": {"one": "{0} time", "other": "{0} timer"}, "minute": {"one": "{0} minutt", "other": "{0} minutter"}, "second": {"one": "{0} sekund", "other": "{0} sekunder"}, "microsecond": {"one": "{0} mikrosekund", "other": "{0} mikrosekunder"}, }, "relative": { "year": { "future": {"other": "om {0} Ã¥r", "one": "om {0} Ã¥r"}, "past": {"other": "for {0} Ã¥r siden", "one": "for {0} Ã¥r siden"}, }, "month": { "future": {"other": "om {0} mÃ¥neder", "one": "om {0} mÃ¥ned"}, "past": { "other": "for {0} mÃ¥neder siden", "one": "for {0} mÃ¥ned siden", }, }, "week": { "future": {"other": "om {0} uker", "one": "om {0} uke"}, "past": {"other": "for {0} uker siden", "one": "for {0} uke siden"}, }, "day": { "future": {"other": "om {0} dager", "one": "om {0} dag"}, "past": {"other": "for {0} dager siden", "one": "for {0} dag siden"}, }, "hour": { "future": {"other": "om {0} timer", "one": "om {0} time"}, "past": {"other": "for {0} timer siden", "one": "for {0} time siden"}, }, "minute": { "future": {"other": "om {0} minutter", "one": "om {0} minutt"}, "past": { "other": "for {0} minutter siden", "one": "for {0} minutt siden", }, }, "second": { "future": {"other": "om {0} sekunder", "one": "om {0} sekund"}, "past": { "other": "for {0} sekunder siden", "one": "for {0} sekund siden", }, }, }, "day_periods": { "midnight": "midnatt", "am": "a.m.", "pm": "p.m.", "morning1": "morgenen", "morning2": "formiddagen", "afternoon1": "ettermiddagen", "evening1": "kvelden", "night1": "natten", }, "week_data": { "min_days": 1, "first_day": 0, "weekend_start": 5, "weekend_end": 6, }, }, "custom": custom_translations, } pendulum-3.0.0/src/pendulum/locales/nl/000077500000000000000000000000001453741033500200235ustar00rootroot00000000000000pendulum-3.0.0/src/pendulum/locales/nl/__init__.py000066400000000000000000000000001453741033500221220ustar00rootroot00000000000000pendulum-3.0.0/src/pendulum/locales/nl/custom.py000066400000000000000000000010331453741033500217040ustar00rootroot00000000000000""" nl custom locale file. """ from __future__ import annotations translations = { "units": {"few_second": "enkele seconden"}, # Relative time "ago": "{} geleden", "from_now": "over {}", "after": "{0} later", "before": "{0} eerder", # Ordinals "ordinal": {"other": "e"}, # Date formats "date_formats": { "L": "DD-MM-YYYY", "LL": "D MMMM YYYY", "LLL": "D MMMM YYYY HH:mm", "LLLL": "dddd D MMMM YYYY HH:mm", "LT": "HH:mm", "LTS": "HH:mm:ss", }, } pendulum-3.0.0/src/pendulum/locales/nl/locale.py000066400000000000000000000111221453741033500216310ustar00rootroot00000000000000from __future__ import annotations from pendulum.locales.nl.custom import translations as custom_translations """ nl locale file. It has been generated automatically and must not be modified directly. """ locale = { "plural": lambda n: "one" if ((n == n and (n == 1)) and (0 == 0 and (0 == 0))) else "other", "ordinal": lambda n: "other", "translations": { "days": { "abbreviated": { 0: "ma", 1: "di", 2: "wo", 3: "do", 4: "vr", 5: "za", 6: "zo", }, "narrow": {0: "M", 1: "D", 2: "W", 3: "D", 4: "V", 5: "Z", 6: "Z"}, "short": {0: "ma", 1: "di", 2: "wo", 3: "do", 4: "vr", 5: "za", 6: "zo"}, "wide": { 0: "maandag", 1: "dinsdag", 2: "woensdag", 3: "donderdag", 4: "vrijdag", 5: "zaterdag", 6: "zondag", }, }, "months": { "abbreviated": { 1: "jan.", 2: "feb.", 3: "mrt.", 4: "apr.", 5: "mei", 6: "jun.", 7: "jul.", 8: "aug.", 9: "sep.", 10: "okt.", 11: "nov.", 12: "dec.", }, "narrow": { 1: "J", 2: "F", 3: "M", 4: "A", 5: "M", 6: "J", 7: "J", 8: "A", 9: "S", 10: "O", 11: "N", 12: "D", }, "wide": { 1: "januari", 2: "februari", 3: "maart", 4: "april", 5: "mei", 6: "juni", 7: "juli", 8: "augustus", 9: "september", 10: "oktober", 11: "november", 12: "december", }, }, "units": { "year": {"one": "{0} jaar", "other": "{0} jaar"}, "month": {"one": "{0} maand", "other": "{0} maanden"}, "week": {"one": "{0} week", "other": "{0} weken"}, "day": {"one": "{0} dag", "other": "{0} dagen"}, "hour": {"one": "{0} uur", "other": "{0} uur"}, "minute": {"one": "{0} minuut", "other": "{0} minuten"}, "second": {"one": "{0} seconde", "other": "{0} seconden"}, "microsecond": {"one": "{0} microseconde", "other": "{0} microseconden"}, }, "relative": { "year": { "future": {"other": "over {0} jaar", "one": "over {0} jaar"}, "past": {"other": "{0} jaar geleden", "one": "{0} jaar geleden"}, }, "month": { "future": {"other": "over {0} maanden", "one": "over {0} maand"}, "past": {"other": "{0} maanden geleden", "one": "{0} maand geleden"}, }, "week": { "future": {"other": "over {0} weken", "one": "over {0} week"}, "past": {"other": "{0} weken geleden", "one": "{0} week geleden"}, }, "day": { "future": {"other": "over {0} dagen", "one": "over {0} dag"}, "past": {"other": "{0} dagen geleden", "one": "{0} dag geleden"}, }, "hour": { "future": {"other": "over {0} uur", "one": "over {0} uur"}, "past": {"other": "{0} uur geleden", "one": "{0} uur geleden"}, }, "minute": { "future": {"other": "over {0} minuten", "one": "over {0} minuut"}, "past": {"other": "{0} minuten geleden", "one": "{0} minuut geleden"}, }, "second": { "future": {"other": "over {0} seconden", "one": "over {0} seconde"}, "past": {"other": "{0} seconden geleden", "one": "{0} seconde geleden"}, }, }, "day_periods": { "midnight": "middernacht", "am": "a.m.", "pm": "p.m.", "morning1": "‘s ochtends", "afternoon1": "‘s middags", "evening1": "‘s avonds", "night1": "‘s nachts", "week_data": { "min_days": 1, "first_day": 0, "weekend_start": 5, "weekend_end": 6, }, }, }, "custom": custom_translations, } pendulum-3.0.0/src/pendulum/locales/nn/000077500000000000000000000000001453741033500200255ustar00rootroot00000000000000pendulum-3.0.0/src/pendulum/locales/nn/__init__.py000066400000000000000000000000001453741033500221240ustar00rootroot00000000000000pendulum-3.0.0/src/pendulum/locales/nn/custom.py000066400000000000000000000007341453741033500217150ustar00rootroot00000000000000""" nn custom locale file. """ from __future__ import annotations translations = { # Relative time "after": "{0} etter", "before": "{0} før", # Ordinals "ordinal": {"one": ".", "two": ".", "few": ".", "other": "."}, # Date formats "date_formats": { "LTS": "HH:mm:ss", "LT": "HH:mm", "LLLL": "dddd Do MMMM YYYY HH:mm", "LLL": "Do MMMM YYYY HH:mm", "LL": "Do MMMM YYYY", "L": "DD.MM.YYYY", }, } pendulum-3.0.0/src/pendulum/locales/nn/locale.py000066400000000000000000000111611453741033500216360ustar00rootroot00000000000000from __future__ import annotations from pendulum.locales.nn.custom import translations as custom_translations """ nn locale file. It has been generated automatically and must not be modified directly. """ locale = { "plural": lambda n: "one" if (n == n and (n == 1)) else "other", "ordinal": lambda n: "other", "translations": { "days": { "abbreviated": { 0: "mÃ¥n.", 1: "tys.", 2: "ons.", 3: "tor.", 4: "fre.", 5: "lau.", 6: "søn.", }, "narrow": {0: "M", 1: "T", 2: "O", 3: "T", 4: "F", 5: "L", 6: "S"}, "short": { 0: "mÃ¥.", 1: "ty.", 2: "on.", 3: "to.", 4: "fr.", 5: "la.", 6: "sø.", }, "wide": { 0: "mÃ¥ndag", 1: "tysdag", 2: "onsdag", 3: "torsdag", 4: "fredag", 5: "laurdag", 6: "søndag", }, }, "months": { "abbreviated": { 1: "jan.", 2: "feb.", 3: "mars", 4: "apr.", 5: "mai", 6: "juni", 7: "juli", 8: "aug.", 9: "sep.", 10: "okt.", 11: "nov.", 12: "des.", }, "narrow": { 1: "J", 2: "F", 3: "M", 4: "A", 5: "M", 6: "J", 7: "J", 8: "A", 9: "S", 10: "O", 11: "N", 12: "D", }, "wide": { 1: "januar", 2: "februar", 3: "mars", 4: "april", 5: "mai", 6: "juni", 7: "juli", 8: "august", 9: "september", 10: "oktober", 11: "november", 12: "desember", }, }, "units": { "year": {"one": "{0} Ã¥r", "other": "{0} Ã¥r"}, "month": {"one": "{0} mÃ¥nad", "other": "{0} mÃ¥nadar"}, "week": {"one": "{0} veke", "other": "{0} veker"}, "day": {"one": "{0} dag", "other": "{0} dagar"}, "hour": {"one": "{0} time", "other": "{0} timar"}, "minute": {"one": "{0} minutt", "other": "{0} minutt"}, "second": {"one": "{0} sekund", "other": "{0} sekund"}, "microsecond": {"one": "{0} mikrosekund", "other": "{0} mikrosekund"}, }, "relative": { "year": { "future": {"other": "om {0} Ã¥r", "one": "om {0} Ã¥r"}, "past": {"other": "for {0} Ã¥r sidan", "one": "for {0} Ã¥r sidan"}, }, "month": { "future": {"other": "om {0} mÃ¥nadar", "one": "om {0} mÃ¥nad"}, "past": { "other": "for {0} mÃ¥nadar sidan", "one": "for {0} mÃ¥nad sidan", }, }, "week": { "future": {"other": "om {0} veker", "one": "om {0} veke"}, "past": {"other": "for {0} veker sidan", "one": "for {0} veke sidan"}, }, "day": { "future": {"other": "om {0} dagar", "one": "om {0} dag"}, "past": {"other": "for {0} dagar sidan", "one": "for {0} dag sidan"}, }, "hour": { "future": {"other": "om {0} timar", "one": "om {0} time"}, "past": {"other": "for {0} timar sidan", "one": "for {0} time sidan"}, }, "minute": { "future": {"other": "om {0} minutt", "one": "om {0} minutt"}, "past": { "other": "for {0} minutt sidan", "one": "for {0} minutt sidan", }, }, "second": { "future": {"other": "om {0} sekund", "one": "om {0} sekund"}, "past": { "other": "for {0} sekund sidan", "one": "for {0} sekund sidan", }, }, }, "day_periods": {"am": "formiddag", "pm": "ettermiddag"}, "week_data": { "min_days": 1, "first_day": 0, "weekend_start": 5, "weekend_end": 6, }, }, "custom": custom_translations, } pendulum-3.0.0/src/pendulum/locales/pl/000077500000000000000000000000001453741033500200255ustar00rootroot00000000000000pendulum-3.0.0/src/pendulum/locales/pl/__init__.py000066400000000000000000000000001453741033500221240ustar00rootroot00000000000000pendulum-3.0.0/src/pendulum/locales/pl/custom.py000066400000000000000000000007421453741033500217140ustar00rootroot00000000000000""" pl custom locale file. """ from __future__ import annotations translations = { "units": {"few_second": "kilka sekund"}, # Relative time "ago": "{} temu", "from_now": "za {}", "after": "{0} po", "before": "{0} przed", # Date formats "date_formats": { "LTS": "HH:mm:ss", "LT": "HH:mm", "L": "DD.MM.YYYY", "LL": "D MMMM YYYY", "LLL": "D MMMM YYYY HH:mm", "LLLL": "dddd, D MMMM YYYY HH:mm", }, } pendulum-3.0.0/src/pendulum/locales/pl/locale.py000066400000000000000000000210261453741033500216370ustar00rootroot00000000000000from __future__ import annotations from pendulum.locales.pl.custom import translations as custom_translations """ pl locale file. It has been generated automatically and must not be modified directly. """ locale = { "plural": lambda n: "few" if ( ( (0 == 0 and (0 == 0)) and ((n % 10) == (n % 10) and ((n % 10) >= 2 and (n % 10) <= 4)) ) and (not ((n % 100) == (n % 100) and ((n % 100) >= 12 and (n % 100) <= 14))) ) else "many" if ( ( ( ((0 == 0 and (0 == 0)) and (not (n == n and (n == 1)))) and ((n % 10) == (n % 10) and ((n % 10) >= 0 and (n % 10) <= 1)) ) or ( (0 == 0 and (0 == 0)) and ((n % 10) == (n % 10) and ((n % 10) >= 5 and (n % 10) <= 9)) ) ) or ( (0 == 0 and (0 == 0)) and ((n % 100) == (n % 100) and ((n % 100) >= 12 and (n % 100) <= 14)) ) ) else "one" if ((n == n and (n == 1)) and (0 == 0 and (0 == 0))) else "other", "ordinal": lambda n: "other", "translations": { "days": { "abbreviated": { 0: "pon.", 1: "wt.", 2: "Å›r.", 3: "czw.", 4: "pt.", 5: "sob.", 6: "niedz.", }, "narrow": {0: "p", 1: "w", 2: "Å›", 3: "c", 4: "p", 5: "s", 6: "n"}, "short": { 0: "pon", 1: "wto", 2: "Å›ro", 3: "czw", 4: "piÄ…", 5: "sob", 6: "nie", }, "wide": { 0: "poniedziaÅ‚ek", 1: "wtorek", 2: "Å›roda", 3: "czwartek", 4: "piÄ…tek", 5: "sobota", 6: "niedziela", }, }, "months": { "abbreviated": { 1: "sty", 2: "lut", 3: "mar", 4: "kwi", 5: "maj", 6: "cze", 7: "lip", 8: "sie", 9: "wrz", 10: "paź", 11: "lis", 12: "gru", }, "narrow": { 1: "s", 2: "l", 3: "m", 4: "k", 5: "m", 6: "c", 7: "l", 8: "s", 9: "w", 10: "p", 11: "l", 12: "g", }, "wide": { 1: "stycznia", 2: "lutego", 3: "marca", 4: "kwietnia", 5: "maja", 6: "czerwca", 7: "lipca", 8: "sierpnia", 9: "wrzeÅ›nia", 10: "października", 11: "listopada", 12: "grudnia", }, }, "units": { "year": { "one": "{0} rok", "few": "{0} lata", "many": "{0} lat", "other": "{0} roku", }, "month": { "one": "{0} miesiÄ…c", "few": "{0} miesiÄ…ce", "many": "{0} miesiÄ™cy", "other": "{0} miesiÄ…ca", }, "week": { "one": "{0} tydzieÅ„", "few": "{0} tygodnie", "many": "{0} tygodni", "other": "{0} tygodnia", }, "day": { "one": "{0} dzieÅ„", "few": "{0} dni", "many": "{0} dni", "other": "{0} dnia", }, "hour": { "one": "{0} godzina", "few": "{0} godziny", "many": "{0} godzin", "other": "{0} godziny", }, "minute": { "one": "{0} minuta", "few": "{0} minuty", "many": "{0} minut", "other": "{0} minuty", }, "second": { "one": "{0} sekunda", "few": "{0} sekundy", "many": "{0} sekund", "other": "{0} sekundy", }, "microsecond": { "one": "{0} mikrosekunda", "few": "{0} mikrosekundy", "many": "{0} mikrosekund", "other": "{0} mikrosekundy", }, }, "relative": { "year": { "future": { "other": "za {0} roku", "one": "za {0} rok", "few": "za {0} lata", "many": "za {0} lat", }, "past": { "other": "{0} roku temu", "one": "{0} rok temu", "few": "{0} lata temu", "many": "{0} lat temu", }, }, "month": { "future": { "other": "za {0} miesiÄ…ca", "one": "za {0} miesiÄ…c", "few": "za {0} miesiÄ…ce", "many": "za {0} miesiÄ™cy", }, "past": { "other": "{0} miesiÄ…ca temu", "one": "{0} miesiÄ…c temu", "few": "{0} miesiÄ…ce temu", "many": "{0} miesiÄ™cy temu", }, }, "week": { "future": { "other": "za {0} tygodnia", "one": "za {0} tydzieÅ„", "few": "za {0} tygodnie", "many": "za {0} tygodni", }, "past": { "other": "{0} tygodnia temu", "one": "{0} tydzieÅ„ temu", "few": "{0} tygodnie temu", "many": "{0} tygodni temu", }, }, "day": { "future": { "other": "za {0} dnia", "one": "za {0} dzieÅ„", "few": "za {0} dni", "many": "za {0} dni", }, "past": { "other": "{0} dnia temu", "one": "{0} dzieÅ„ temu", "few": "{0} dni temu", "many": "{0} dni temu", }, }, "hour": { "future": { "other": "za {0} godziny", "one": "za {0} godzinÄ™", "few": "za {0} godziny", "many": "za {0} godzin", }, "past": { "other": "{0} godziny temu", "one": "{0} godzinÄ™ temu", "few": "{0} godziny temu", "many": "{0} godzin temu", }, }, "minute": { "future": { "other": "za {0} minuty", "one": "za {0} minutÄ™", "few": "za {0} minuty", "many": "za {0} minut", }, "past": { "other": "{0} minuty temu", "one": "{0} minutÄ™ temu", "few": "{0} minuty temu", "many": "{0} minut temu", }, }, "second": { "future": { "other": "za {0} sekundy", "one": "za {0} sekundÄ™", "few": "za {0} sekundy", "many": "za {0} sekund", }, "past": { "other": "{0} sekundy temu", "one": "{0} sekundÄ™ temu", "few": "{0} sekundy temu", "many": "{0} sekund temu", }, }, }, "day_periods": { "midnight": "o północy", "am": "AM", "noon": "w poÅ‚udnie", "pm": "PM", "morning1": "rano", "morning2": "przed poÅ‚udniem", "afternoon1": "po poÅ‚udniu", "evening1": "wieczorem", "night1": "w nocy", }, "week_data": { "min_days": 1, "first_day": 0, "weekend_start": 5, "weekend_end": 6, }, }, "custom": custom_translations, } pendulum-3.0.0/src/pendulum/locales/pt_br/000077500000000000000000000000001453741033500205205ustar00rootroot00000000000000pendulum-3.0.0/src/pendulum/locales/pt_br/__init__.py000066400000000000000000000000001453741033500226170ustar00rootroot00000000000000pendulum-3.0.0/src/pendulum/locales/pt_br/custom.py000066400000000000000000000006671453741033500224150ustar00rootroot00000000000000""" pt-br custom locale file. """ from __future__ import annotations translations = { # Relative time "after": "após {0}", "before": "{0} atrás", # Date formats "date_formats": { "LTS": "HH:mm:ss", "LT": "HH:mm", "LLLL": "dddd, D [de] MMMM [de] YYYY [às] HH:mm", "LLL": "D [de] MMMM [de] YYYY [às] HH:mm", "LL": "D [de] MMMM [de] YYYY", "L": "DD/MM/YYYY", }, } pendulum-3.0.0/src/pendulum/locales/pt_br/locale.py000066400000000000000000000112621453741033500223330ustar00rootroot00000000000000from __future__ import annotations from pendulum.locales.pt_br.custom import translations as custom_translations """ pt_br locale file. It has been generated automatically and must not be modified directly. """ locale = { "plural": lambda n: "one" if ((n == n and (n >= 0 and n <= 2)) and (not (n == n and (n == 2)))) else "other", "ordinal": lambda n: "other", "translations": { "days": { "abbreviated": { 0: "seg", 1: "ter", 2: "qua", 3: "qui", 4: "sex", 5: "sáb", 6: "dom", }, "narrow": {0: "S", 1: "T", 2: "Q", 3: "Q", 4: "S", 5: "S", 6: "D"}, "short": { 0: "seg", 1: "ter", 2: "qua", 3: "qui", 4: "sex", 5: "sáb", 6: "dom", }, "wide": { 0: "segunda-feira", 1: "terça-feira", 2: "quarta-feira", 3: "quinta-feira", 4: "sexta-feira", 5: "sábado", 6: "domingo", }, }, "months": { "abbreviated": { 1: "jan", 2: "fev", 3: "mar", 4: "abr", 5: "mai", 6: "jun", 7: "jul", 8: "ago", 9: "set", 10: "out", 11: "nov", 12: "dez", }, "narrow": { 1: "J", 2: "F", 3: "M", 4: "A", 5: "M", 6: "J", 7: "J", 8: "A", 9: "S", 10: "O", 11: "N", 12: "D", }, "wide": { 1: "janeiro", 2: "fevereiro", 3: "março", 4: "abril", 5: "maio", 6: "junho", 7: "julho", 8: "agosto", 9: "setembro", 10: "outubro", 11: "novembro", 12: "dezembro", }, }, "units": { "year": {"one": "{0} ano", "other": "{0} anos"}, "month": {"one": "{0} mês", "other": "{0} meses"}, "week": {"one": "{0} semana", "other": "{0} semanas"}, "day": {"one": "{0} dia", "other": "{0} dias"}, "hour": {"one": "{0} hora", "other": "{0} horas"}, "minute": {"one": "{0} minuto", "other": "{0} minutos"}, "second": {"one": "{0} segundo", "other": "{0} segundos"}, "microsecond": {"one": "{0} microssegundo", "other": "{0} microssegundos"}, }, "relative": { "year": { "future": {"other": "em {0} anos", "one": "em {0} ano"}, "past": {"other": "há {0} anos", "one": "há {0} ano"}, }, "month": { "future": {"other": "em {0} meses", "one": "em {0} mês"}, "past": {"other": "há {0} meses", "one": "há {0} mês"}, }, "week": { "future": {"other": "em {0} semanas", "one": "em {0} semana"}, "past": {"other": "há {0} semanas", "one": "há {0} semana"}, }, "day": { "future": {"other": "em {0} dias", "one": "em {0} dia"}, "past": {"other": "há {0} dias", "one": "há {0} dia"}, }, "hour": { "future": {"other": "em {0} horas", "one": "em {0} hora"}, "past": {"other": "há {0} horas", "one": "há {0} hora"}, }, "minute": { "future": {"other": "em {0} minutos", "one": "em {0} minuto"}, "past": {"other": "há {0} minutos", "one": "há {0} minuto"}, }, "second": { "future": {"other": "em {0} segundos", "one": "em {0} segundo"}, "past": {"other": "há {0} segundos", "one": "há {0} segundo"}, }, }, "day_periods": { "midnight": "meia-noite", "am": "AM", "noon": "meio-dia", "pm": "PM", "morning1": "da manhã", "afternoon1": "da tarde", "evening1": "da noite", "night1": "da madrugada", }, "week_data": { "min_days": 1, "first_day": 6, "weekend_start": 5, "weekend_end": 6, }, }, "custom": custom_translations, } pendulum-3.0.0/src/pendulum/locales/ru/000077500000000000000000000000001453741033500200405ustar00rootroot00000000000000pendulum-3.0.0/src/pendulum/locales/ru/__init__.py000066400000000000000000000000001453741033500221370ustar00rootroot00000000000000pendulum-3.0.0/src/pendulum/locales/ru/custom.py000066400000000000000000000007301453741033500217240ustar00rootroot00000000000000""" ru custom locale file. """ from __future__ import annotations translations = { # Relative time "ago": "{} назад", "from_now": "через {}", "after": "{0} поÑле", "before": "{0} до", # Date formats "date_formats": { "LTS": "HH:mm:ss", "LT": "HH:mm", "L": "DD.MM.YYYY", "LL": "D MMMM YYYY г.", "LLL": "D MMMM YYYY г., HH:mm", "LLLL": "dddd, D MMMM YYYY г., HH:mm", }, } pendulum-3.0.0/src/pendulum/locales/ru/locale.py000066400000000000000000000224711453741033500216570ustar00rootroot00000000000000from __future__ import annotations from pendulum.locales.ru.custom import translations as custom_translations """ ru locale file. It has been generated automatically and must not be modified directly. """ locale = { "plural": lambda n: "few" if ( ( (0 == 0 and (0 == 0)) and ((n % 10) == (n % 10) and ((n % 10) >= 2 and (n % 10) <= 4)) ) and (not ((n % 100) == (n % 100) and ((n % 100) >= 12 and (n % 100) <= 14))) ) else "many" if ( ( ((0 == 0 and (0 == 0)) and ((n % 10) == (n % 10) and ((n % 10) == 0))) or ( (0 == 0 and (0 == 0)) and ((n % 10) == (n % 10) and ((n % 10) >= 5 and (n % 10) <= 9)) ) ) or ( (0 == 0 and (0 == 0)) and ((n % 100) == (n % 100) and ((n % 100) >= 11 and (n % 100) <= 14)) ) ) else "one" if ( ((0 == 0 and (0 == 0)) and ((n % 10) == (n % 10) and ((n % 10) == 1))) and (not ((n % 100) == (n % 100) and ((n % 100) == 11))) ) else "other", "ordinal": lambda n: "other", "translations": { "days": { "abbreviated": { 0: "пн", 1: "вт", 2: "ÑÑ€", 3: "чт", 4: "пт", 5: "Ñб", 6: "вÑ", }, "narrow": {0: "пн", 1: "вт", 2: "ÑÑ€", 3: "чт", 4: "пт", 5: "Ñб", 6: "вÑ"}, "short": {0: "пн", 1: "вт", 2: "ÑÑ€", 3: "чт", 4: "пт", 5: "Ñб", 6: "вÑ"}, "wide": { 0: "понедельник", 1: "вторник", 2: "Ñреда", 3: "четверг", 4: "пÑтница", 5: "Ñуббота", 6: "воÑкреÑенье", }, }, "months": { "abbreviated": { 1: "Ñнв.", 2: "февр.", 3: "мар.", 4: "апр.", 5: "маÑ", 6: "июн.", 7: "июл.", 8: "авг.", 9: "Ñент.", 10: "окт.", 11: "ноÑб.", 12: "дек.", }, "narrow": { 1: "Я", 2: "Ф", 3: "М", 4: "Ð", 5: "М", 6: "И", 7: "И", 8: "Ð", 9: "С", 10: "О", 11: "Ð", 12: "Д", }, "wide": { 1: "ÑнварÑ", 2: "февралÑ", 3: "марта", 4: "апрелÑ", 5: "маÑ", 6: "июнÑ", 7: "июлÑ", 8: "авгуÑта", 9: "ÑентÑбрÑ", 10: "октÑбрÑ", 11: "ноÑбрÑ", 12: "декабрÑ", }, }, "units": { "year": { "one": "{0} год", "few": "{0} года", "many": "{0} лет", "other": "{0} года", }, "month": { "one": "{0} меÑÑц", "few": "{0} меÑÑца", "many": "{0} меÑÑцев", "other": "{0} меÑÑца", }, "week": { "one": "{0} неделÑ", "few": "{0} недели", "many": "{0} недель", "other": "{0} недели", }, "day": { "one": "{0} день", "few": "{0} днÑ", "many": "{0} дней", "other": "{0} днÑ", }, "hour": { "one": "{0} чаÑ", "few": "{0} чаÑа", "many": "{0} чаÑов", "other": "{0} чаÑа", }, "minute": { "one": "{0} минута", "few": "{0} минуты", "many": "{0} минут", "other": "{0} минуты", }, "second": { "one": "{0} Ñекунда", "few": "{0} Ñекунды", "many": "{0} Ñекунд", "other": "{0} Ñекунды", }, "microsecond": { "one": "{0} микроÑекунда", "few": "{0} микроÑекунды", "many": "{0} микроÑекунд", "other": "{0} микроÑекунды", }, }, "relative": { "year": { "future": { "other": "через {0} года", "one": "через {0} год", "few": "через {0} года", "many": "через {0} лет", }, "past": { "other": "{0} года назад", "one": "{0} год назад", "few": "{0} года назад", "many": "{0} лет назад", }, }, "month": { "future": { "other": "через {0} меÑÑца", "one": "через {0} меÑÑц", "few": "через {0} меÑÑца", "many": "через {0} меÑÑцев", }, "past": { "other": "{0} меÑÑца назад", "one": "{0} меÑÑц назад", "few": "{0} меÑÑца назад", "many": "{0} меÑÑцев назад", }, }, "week": { "future": { "other": "через {0} недели", "one": "через {0} неделю", "few": "через {0} недели", "many": "через {0} недель", }, "past": { "other": "{0} недели назад", "one": "{0} неделю назад", "few": "{0} недели назад", "many": "{0} недель назад", }, }, "day": { "future": { "other": "через {0} днÑ", "one": "через {0} день", "few": "через {0} днÑ", "many": "через {0} дней", }, "past": { "other": "{0} Ð´Ð½Ñ Ð½Ð°Ð·Ð°Ð´", "one": "{0} день назад", "few": "{0} Ð´Ð½Ñ Ð½Ð°Ð·Ð°Ð´", "many": "{0} дней назад", }, }, "hour": { "future": { "other": "через {0} чаÑа", "one": "через {0} чаÑ", "few": "через {0} чаÑа", "many": "через {0} чаÑов", }, "past": { "other": "{0} чаÑа назад", "one": "{0} Ñ‡Ð°Ñ Ð½Ð°Ð·Ð°Ð´", "few": "{0} чаÑа назад", "many": "{0} чаÑов назад", }, }, "minute": { "future": { "other": "через {0} минуты", "one": "через {0} минуту", "few": "через {0} минуты", "many": "через {0} минут", }, "past": { "other": "{0} минуты назад", "one": "{0} минуту назад", "few": "{0} минуты назад", "many": "{0} минут назад", }, }, "second": { "future": { "other": "через {0} Ñекунды", "one": "через {0} Ñекунду", "few": "через {0} Ñекунды", "many": "через {0} Ñекунд", }, "past": { "other": "{0} Ñекунды назад", "one": "{0} Ñекунду назад", "few": "{0} Ñекунды назад", "many": "{0} Ñекунд назад", }, }, }, "day_periods": { "midnight": "полночь", "am": "AM", "noon": "полдень", "pm": "PM", "morning1": "утра", "afternoon1": "днÑ", "evening1": "вечера", "night1": "ночи", }, "week_data": { "min_days": 1, "first_day": 0, "weekend_start": 5, "weekend_end": 6, }, }, "custom": custom_translations, } pendulum-3.0.0/src/pendulum/locales/sk/000077500000000000000000000000001453741033500200275ustar00rootroot00000000000000pendulum-3.0.0/src/pendulum/locales/sk/__init__.py000066400000000000000000000000001453741033500221260ustar00rootroot00000000000000pendulum-3.0.0/src/pendulum/locales/sk/custom.py000066400000000000000000000006661453741033500217230ustar00rootroot00000000000000""" sk custom locale file. """ from __future__ import annotations translations = { # Relative time "ago": "pred {}", "from_now": "o {}", "after": "{0} po", "before": "{0} pred", # Date formats "date_formats": { "LTS": "HH:mm:ss", "LT": "HH:mm", "LLLL": "dddd, D. MMMM YYYY HH:mm", "LLL": "D. MMMM YYYY HH:mm", "LL": "D. MMMM YYYY", "L": "DD.MM.YYYY", }, } pendulum-3.0.0/src/pendulum/locales/sk/locale.py000066400000000000000000000177621453741033500216550ustar00rootroot00000000000000from __future__ import annotations from pendulum.locales.sk.custom import translations as custom_translations """ sk locale file. It has been generated automatically and must not be modified directly. """ locale = { "plural": lambda n: "few" if ((n == n and (n >= 2 and n <= 4)) and (0 == 0 and (0 == 0))) else "many" if (not (0 == 0 and (0 == 0))) else "one" if ((n == n and (n == 1)) and (0 == 0 and (0 == 0))) else "other", "ordinal": lambda n: "other", "translations": { "days": { "abbreviated": { 0: "po", 1: "ut", 2: "st", 3: "Å¡t", 4: "pi", 5: "so", 6: "ne", }, "narrow": { 0: "p", 1: "u", 2: "s", 3: "Å¡", 4: "p", 5: "s", 6: "n", }, "short": { 0: "po", 1: "ut", 2: "st", 3: "Å¡t", 4: "pi", 5: "so", 6: "ne", }, "wide": { 0: "pondelok", 1: "utorok", 2: "streda", 3: "Å¡tvrtok", 4: "piatok", 5: "sobota", 6: "nedeľa", }, }, "months": { "abbreviated": { 1: "jan", 2: "feb", 3: "mar", 4: "apr", 5: "máj", 6: "jún", 7: "júl", 8: "aug", 9: "sep", 10: "okt", 11: "nov", 12: "dec", }, "narrow": { 1: "j", 2: "f", 3: "m", 4: "a", 5: "m", 6: "j", 7: "j", 8: "a", 9: "s", 10: "o", 11: "n", 12: "d", }, "wide": { 1: "januára", 2: "februára", 3: "marca", 4: "apríla", 5: "mája", 6: "júna", 7: "júla", 8: "augusta", 9: "septembra", 10: "októbra", 11: "novembra", 12: "decembra", }, }, "units": { "year": { "one": "{0} rok", "few": "{0} roky", "many": "{0} roka", "other": "{0} rokov", }, "month": { "one": "{0} mesiac", "few": "{0} mesiace", "many": "{0} mesiaca", "other": "{0} mesiacov", }, "week": { "one": "{0} týždeň", "few": "{0} týždne", "many": "{0} týždňa", "other": "{0} týždňov", }, "day": { "one": "{0} deň", "few": "{0} dni", "many": "{0} dňa", "other": "{0} dní", }, "hour": { "one": "{0} hodina", "few": "{0} hodiny", "many": "{0} hodiny", "other": "{0} hodín", }, "minute": { "one": "{0} minúta", "few": "{0} minúty", "many": "{0} minúty", "other": "{0} minút", }, "second": { "one": "{0} sekunda", "few": "{0} sekundy", "many": "{0} sekundy", "other": "{0} sekúnd", }, "microsecond": { "one": "{0} mikrosekunda", "few": "{0} mikrosekundy", "many": "{0} mikrosekundy", "other": "{0} mikrosekúnd", }, }, "relative": { "year": { "future": { "other": "o {0} rokov", "one": "o {0} rok", "few": "o {0} roky", "many": "o {0} roka", }, "past": { "other": "pred {0} rokmi", "one": "pred {0} rokom", "few": "pred {0} rokmi", "many": "pred {0} roka", }, }, "month": { "future": { "other": "o {0} mesiacov", "one": "o {0} mesiac", "few": "o {0} mesiace", "many": "o {0} mesiaca", }, "past": { "other": "pred {0} mesiacmi", "one": "pred {0} mesiacom", "few": "pred {0} mesiacmi", "many": "pred {0} mesiaca", }, }, "week": { "future": { "other": "o {0} týždňov", "one": "o {0} týždeň", "few": "o {0} týždne", "many": "o {0} týždňa", }, "past": { "other": "pred {0} týždňami", "one": "pred {0} týždňom", "few": "pred {0} týždňami", "many": "pred {0} týždňa", }, }, "day": { "future": { "other": "o {0} dní", "one": "o {0} deň", "few": "o {0} dni", "many": "o {0} dňa", }, "past": { "other": "pred {0} dňami", "one": "pred {0} dňom", "few": "pred {0} dňami", "many": "pred {0} dňa", }, }, "hour": { "future": { "other": "o {0} hodín", "one": "o {0} hodinu", "few": "o {0} hodiny", "many": "o {0} hodiny", }, "past": { "other": "pred {0} hodinami", "one": "pred {0} hodinou", "few": "pred {0} hodinami", "many": "pred {0} hodinou", }, }, "minute": { "future": { "other": "o {0} minút", "one": "o {0} minútu", "few": "o {0} minúty", "many": "o {0} minúty", }, "past": { "other": "pred {0} minútami", "one": "pred {0} minútou", "few": "pred {0} minútami", "many": "pred {0} minúty", }, }, "second": { "future": { "other": "o {0} sekúnd", "one": "o {0} sekundu", "few": "o {0} sekundy", "many": "o {0} sekundy", }, "past": { "other": "pred {0} sekundami", "one": "pred {0} sekundou", "few": "pred {0} sekundami", "many": "pred {0} sekundy", }, }, }, "day_periods": { "midnight": "o polnoci", "am": "AM", "noon": "napoludnie", "pm": "PM", "morning1": "ráno", "morning2": "dopoludnia", "afternoon1": "popoludní", "evening1": "veÄer", "night1": "v noci", }, "week_data": { "min_days": 1, "first_day": 0, "weekend_start": 5, "weekend_end": 6, }, }, "custom": custom_translations, } pendulum-3.0.0/src/pendulum/locales/sv/000077500000000000000000000000001453741033500200425ustar00rootroot00000000000000pendulum-3.0.0/src/pendulum/locales/sv/__init__.py000066400000000000000000000000001453741033500221410ustar00rootroot00000000000000pendulum-3.0.0/src/pendulum/locales/sv/custom.py000066400000000000000000000007011453741033500217240ustar00rootroot00000000000000""" sv custom locale file. """ from __future__ import annotations translations = { # Relative time "ago": "{} sedan", "from_now": "frÃ¥n nu {}", "after": "{0} efter", "before": "{0} innan", # Date formats "date_formats": { "LTS": "HH:mm:ss", "LT": "HH:mm", "L": "YYYY-MM-DD", "LL": "D MMMM YYYY", "LLL": "D MMMM YYYY, HH:mm", "LLLL": "dddd, D MMMM YYYY, HH:mm", }, } pendulum-3.0.0/src/pendulum/locales/sv/locale.py000066400000000000000000000143621453741033500216610ustar00rootroot00000000000000from __future__ import annotations from pendulum.locales.sv.custom import translations as custom_translations """ sv locale file. It has been generated automatically and must not be modified directly. """ locale = { "plural": lambda n: "one" if ((n == n and (n == 1)) and (0 == 0 and (0 == 0))) else "other", "ordinal": lambda n: "one" if ( ((n % 10) == (n % 10) and (((n % 10) == 1) or ((n % 10) == 2))) and (not ((n % 100) == (n % 100) and (((n % 100) == 11) or ((n % 100) == 12)))) ) else "other", "translations": { "days": { "abbreviated": { 0: "mÃ¥n", 1: "tis", 2: "ons", 3: "tors", 4: "fre", 5: "lör", 6: "sön", }, "narrow": { 0: "M", 1: "T", 2: "O", 3: "T", 4: "F", 5: "L", 6: "S", }, "short": { 0: "mÃ¥", 1: "ti", 2: "on", 3: "to", 4: "fr", 5: "lö", 6: "sö", }, "wide": { 0: "mÃ¥ndag", 1: "tisdag", 2: "onsdag", 3: "torsdag", 4: "fredag", 5: "lördag", 6: "söndag", }, }, "months": { "abbreviated": { 1: "jan.", 2: "feb.", 3: "mars", 4: "apr.", 5: "maj", 6: "juni", 7: "juli", 8: "aug.", 9: "sep.", 10: "okt.", 11: "nov.", 12: "dec.", }, "narrow": { 1: "J", 2: "F", 3: "M", 4: "A", 5: "M", 6: "J", 7: "J", 8: "A", 9: "S", 10: "O", 11: "N", 12: "D", }, "wide": { 1: "januari", 2: "februari", 3: "mars", 4: "april", 5: "maj", 6: "juni", 7: "juli", 8: "augusti", 9: "september", 10: "oktober", 11: "november", 12: "december", }, }, "units": { "year": { "one": "{0} Ã¥r", "other": "{0} Ã¥r", }, "month": { "one": "{0} mÃ¥nad", "other": "{0} mÃ¥nader", }, "week": { "one": "{0} vecka", "other": "{0} veckor", }, "day": { "one": "{0} dygn", "other": "{0} dygn", }, "hour": { "one": "{0} timme", "other": "{0} timmar", }, "minute": { "one": "{0} minut", "other": "{0} minuter", }, "second": { "one": "{0} sekund", "other": "{0} sekunder", }, "microsecond": { "one": "{0} mikrosekund", "other": "{0} mikrosekunder", }, }, "relative": { "year": { "future": { "other": "om {0} Ã¥r", "one": "om {0} Ã¥r", }, "past": { "other": "för {0} Ã¥r sedan", "one": "för {0} Ã¥r sedan", }, }, "month": { "future": { "other": "om {0} mÃ¥nader", "one": "om {0} mÃ¥nad", }, "past": { "other": "för {0} mÃ¥nader sedan", "one": "för {0} mÃ¥nad sedan", }, }, "week": { "future": { "other": "om {0} veckor", "one": "om {0} vecka", }, "past": { "other": "för {0} veckor sedan", "one": "för {0} vecka sedan", }, }, "day": { "future": { "other": "om {0} dagar", "one": "om {0} dag", }, "past": { "other": "för {0} dagar sedan", "one": "för {0} dag sedan", }, }, "hour": { "future": { "other": "om {0} timmar", "one": "om {0} timme", }, "past": { "other": "för {0} timmar sedan", "one": "för {0} timme sedan", }, }, "minute": { "future": { "other": "om {0} minuter", "one": "om {0} minut", }, "past": { "other": "för {0} minuter sedan", "one": "för {0} minut sedan", }, }, "second": { "future": { "other": "om {0} sekunder", "one": "om {0} sekund", }, "past": { "other": "för {0} sekunder sedan", "one": "för {0} sekund sedan", }, }, }, "day_periods": { "midnight": "midnatt", "am": "fm", "pm": "em", "morning1": "pÃ¥ morgonen", "morning2": "pÃ¥ förmiddagen", "afternoon1": "pÃ¥ eftermiddagen", "evening1": "pÃ¥ kvällen", "night1": "pÃ¥ natten", }, "week_data": { "min_days": 1, "first_day": 0, "weekend_start": 5, "weekend_end": 6, }, }, "custom": custom_translations, } pendulum-3.0.0/src/pendulum/locales/tr/000077500000000000000000000000001453741033500200375ustar00rootroot00000000000000pendulum-3.0.0/src/pendulum/locales/tr/__init__.py000066400000000000000000000000001453741033500221360ustar00rootroot00000000000000pendulum-3.0.0/src/pendulum/locales/tr/custom.py000066400000000000000000000010271453741033500217230ustar00rootroot00000000000000""" tr custom locale file. """ from __future__ import annotations translations = { # Relative time "ago": "{} önce", "from_now": "{} içinde", "after": "{0} sonra", "before": "{0} önce", # Ordinals "ordinal": {"one": ".", "two": ".", "few": ".", "other": "."}, # Date formats "date_formats": { "LTS": "h:mm:ss A", "LT": "h:mm A", "L": "MM/DD/YYYY", "LL": "MMMM D, YYYY", "LLL": "MMMM D, YYYY h:mm A", "LLLL": "dddd, MMMM D, YYYY h:mm A", }, } pendulum-3.0.0/src/pendulum/locales/tr/locale.py000066400000000000000000000137671453741033500216660ustar00rootroot00000000000000from __future__ import annotations from pendulum.locales.tr.custom import translations as custom_translations """ tr locale file. It has been generated automatically and must not be modified directly. """ locale = { "plural": lambda n: "one" if (n == n and (n == 1)) else "other", "ordinal": lambda n: "other", "translations": { "days": { "abbreviated": { 0: "Pzt", 1: "Sal", 2: "Çar", 3: "Per", 4: "Cum", 5: "Cmt", 6: "Paz", }, "narrow": { 0: "P", 1: "S", 2: "Ç", 3: "P", 4: "C", 5: "C", 6: "P", }, "short": { 0: "Pt", 1: "Sa", 2: "Ça", 3: "Pe", 4: "Cu", 5: "Ct", 6: "Pa", }, "wide": { 0: "Pazartesi", 1: "Salı", 2: "ÇarÅŸamba", 3: "PerÅŸembe", 4: "Cuma", 5: "Cumartesi", 6: "Pazar", }, }, "months": { "abbreviated": { 1: "Oca", 2: "Åžub", 3: "Mar", 4: "Nis", 5: "May", 6: "Haz", 7: "Tem", 8: "AÄŸu", 9: "Eyl", 10: "Eki", 11: "Kas", 12: "Ara", }, "narrow": { 1: "O", 2: "Åž", 3: "M", 4: "N", 5: "M", 6: "H", 7: "T", 8: "A", 9: "E", 10: "E", 11: "K", 12: "A", }, "wide": { 1: "Ocak", 2: "Åžubat", 3: "Mart", 4: "Nisan", 5: "Mayıs", 6: "Haziran", 7: "Temmuz", 8: "AÄŸustos", 9: "Eylül", 10: "Ekim", 11: "Kasım", 12: "Aralık", }, }, "units": { "year": { "one": "{0} yıl", "other": "{0} yıl", }, "month": { "one": "{0} ay", "other": "{0} ay", }, "week": { "one": "{0} hafta", "other": "{0} hafta", }, "day": { "one": "{0} gün", "other": "{0} gün", }, "hour": { "one": "{0} saat", "other": "{0} saat", }, "minute": { "one": "{0} dakika", "other": "{0} dakika", }, "second": { "one": "{0} saniye", "other": "{0} saniye", }, "microsecond": { "one": "{0} mikrosaniye", "other": "{0} mikrosaniye", }, }, "relative": { "year": { "future": { "other": "{0} yıl sonra", "one": "{0} yıl sonra", }, "past": { "other": "{0} yıl önce", "one": "{0} yıl önce", }, }, "month": { "future": { "other": "{0} ay sonra", "one": "{0} ay sonra", }, "past": { "other": "{0} ay önce", "one": "{0} ay önce", }, }, "week": { "future": { "other": "{0} hafta sonra", "one": "{0} hafta sonra", }, "past": { "other": "{0} hafta önce", "one": "{0} hafta önce", }, }, "day": { "future": { "other": "{0} gün sonra", "one": "{0} gün sonra", }, "past": { "other": "{0} gün önce", "one": "{0} gün önce", }, }, "hour": { "future": { "other": "{0} saat sonra", "one": "{0} saat sonra", }, "past": { "other": "{0} saat önce", "one": "{0} saat önce", }, }, "minute": { "future": { "other": "{0} dakika sonra", "one": "{0} dakika sonra", }, "past": { "other": "{0} dakika önce", "one": "{0} dakika önce", }, }, "second": { "future": { "other": "{0} saniye sonra", "one": "{0} saniye sonra", }, "past": { "other": "{0} saniye önce", "one": "{0} saniye önce", }, }, }, "day_periods": { "midnight": "gece yarısı", "am": "ÖÖ", "noon": "öğle", "pm": "ÖS", "morning1": "sabah", "morning2": "öğleden önce", "afternoon1": "öğleden sonra", "afternoon2": "akÅŸamüstü", "evening1": "akÅŸam", "night1": "gece", }, "week_data": { "min_days": 1, "first_day": 0, "weekend_start": 5, "weekend_end": 6, }, }, "custom": custom_translations, } pendulum-3.0.0/src/pendulum/locales/zh/000077500000000000000000000000001453741033500200335ustar00rootroot00000000000000pendulum-3.0.0/src/pendulum/locales/zh/__init__.py000066400000000000000000000000001453741033500221320ustar00rootroot00000000000000pendulum-3.0.0/src/pendulum/locales/zh/custom.py000066400000000000000000000006421453741033500217210ustar00rootroot00000000000000""" zh custom locale file. """ from __future__ import annotations translations = { # Relative time "after": "{time}åŽ", "before": "{time}å‰", # Date formats "date_formats": { "LTS": "Ah点m分sç§’", "LT": "Ah点mm分", "LLLL": "YYYYå¹´MMMDæ—¥ddddAh点mm分", "LLL": "YYYYå¹´MMMDæ—¥Ah点mm分", "LL": "YYYYå¹´MMMDæ—¥", "L": "YYYY-MM-DD", }, } pendulum-3.0.0/src/pendulum/locales/zh/locale.py000066400000000000000000000073001453741033500216440ustar00rootroot00000000000000from __future__ import annotations from pendulum.locales.zh.custom import translations as custom_translations """ zh locale file. It has been generated automatically and must not be modified directly. """ locale = { "plural": lambda n: "other", "ordinal": lambda n: "other", "translations": { "days": { "abbreviated": { 0: "周一", 1: "周二", 2: "周三", 3: "周四", 4: "周五", 5: "周六", 6: "周日", }, "narrow": {0: "一", 1: "二", 2: "三", 3: "å››", 4: "五", 5: "å…­", 6: "æ—¥"}, "short": {0: "周一", 1: "周二", 2: "周三", 3: "周四", 4: "周五", 5: "周六", 6: "周日"}, "wide": { 0: "星期一", 1: "星期二", 2: "星期三", 3: "星期四", 4: "星期五", 5: "星期六", 6: "星期日", }, }, "months": { "abbreviated": { 1: "1月", 2: "2月", 3: "3月", 4: "4月", 5: "5月", 6: "6月", 7: "7月", 8: "8月", 9: "9月", 10: "10月", 11: "11月", 12: "12月", }, "narrow": { 1: "1", 2: "2", 3: "3", 4: "4", 5: "5", 6: "6", 7: "7", 8: "8", 9: "9", 10: "10", 11: "11", 12: "12", }, "wide": { 1: "一月", 2: "二月", 3: "三月", 4: "四月", 5: "五月", 6: "六月", 7: "七月", 8: "八月", 9: "乿œˆ", 10: "åæœˆ", 11: "å一月", 12: "å二月", }, }, "units": { "year": {"other": "{0}å¹´"}, "month": {"other": "{0}个月"}, "week": {"other": "{0}周"}, "day": {"other": "{0}天"}, "hour": {"other": "{0}å°æ—¶"}, "minute": {"other": "{0}分钟"}, "second": {"other": "{0}ç§’é’Ÿ"}, "microsecond": {"other": "{0}微秒"}, }, "relative": { "year": {"future": {"other": "{0}å¹´åŽ"}, "past": {"other": "{0}å¹´å‰"}}, "month": {"future": {"other": "{0}个月åŽ"}, "past": {"other": "{0}个月å‰"}}, "week": {"future": {"other": "{0}周åŽ"}, "past": {"other": "{0}周å‰"}}, "day": {"future": {"other": "{0}天åŽ"}, "past": {"other": "{0}天å‰"}}, "hour": {"future": {"other": "{0}å°æ—¶åŽ"}, "past": {"other": "{0}å°æ—¶å‰"}}, "minute": {"future": {"other": "{0}分钟åŽ"}, "past": {"other": "{0}分钟å‰"}}, "second": {"future": {"other": "{0}ç§’é’ŸåŽ"}, "past": {"other": "{0}ç§’é’Ÿå‰"}}, }, "day_periods": { "midnight": "åˆå¤œ", "am": "上åˆ", "pm": "下åˆ", "morning1": "清晨", "morning2": "上åˆ", "afternoon1": "下åˆ", "afternoon2": "下åˆ", "evening1": "晚上", "night1": "凌晨", }, "week_data": { "min_days": 1, "first_day": 0, "weekend_start": 5, "weekend_end": 6, }, }, "custom": custom_translations, } pendulum-3.0.0/src/pendulum/mixins/000077500000000000000000000000001453741033500172775ustar00rootroot00000000000000pendulum-3.0.0/src/pendulum/mixins/__init__.py000066400000000000000000000000001453741033500213760ustar00rootroot00000000000000pendulum-3.0.0/src/pendulum/mixins/default.py000066400000000000000000000016131453741033500212760ustar00rootroot00000000000000from __future__ import annotations from pendulum.formatting import Formatter _formatter = Formatter() class FormattableMixin: _formatter: Formatter = _formatter def format(self, fmt: str, locale: str | None = None) -> str: """ Formats the instance using the given format. :param fmt: The format to use :param locale: The locale to use """ return self._formatter.format(self, fmt, locale) def for_json(self) -> str: """ Methods for automatic json serialization by simplejson. """ return self.isoformat() def __format__(self, format_spec: str) -> str: if len(format_spec) > 0: if "%" in format_spec: return self.strftime(format_spec) return self.format(format_spec) return str(self) def __str__(self) -> str: return self.isoformat() pendulum-3.0.0/src/pendulum/parser.py000066400000000000000000000074571453741033500176530ustar00rootroot00000000000000from __future__ import annotations import datetime import typing as t import pendulum from pendulum.duration import Duration from pendulum.parsing import _Interval from pendulum.parsing import parse as base_parse from pendulum.tz.timezone import UTC if t.TYPE_CHECKING: from pendulum.date import Date from pendulum.datetime import DateTime from pendulum.interval import Interval from pendulum.time import Time try: from pendulum._pendulum import Duration as RustDuration except ImportError: RustDuration = None # type: ignore[assignment,misc] def parse(text: str, **options: t.Any) -> Date | Time | DateTime | Duration: # Use the mock now value if it exists options["now"] = options.get("now") return _parse(text, **options) def _parse(text: str, **options: t.Any) -> Date | DateTime | Time | Duration | Interval: """ Parses a string with the given options. :param text: The string to parse. """ # Handling special cases if text == "now": return pendulum.now() parsed = base_parse(text, **options) if isinstance(parsed, datetime.datetime): return pendulum.datetime( parsed.year, parsed.month, parsed.day, parsed.hour, parsed.minute, parsed.second, parsed.microsecond, tz=parsed.tzinfo or options.get("tz", UTC), ) if isinstance(parsed, datetime.date): return pendulum.date(parsed.year, parsed.month, parsed.day) if isinstance(parsed, datetime.time): return pendulum.time( parsed.hour, parsed.minute, parsed.second, parsed.microsecond ) if isinstance(parsed, _Interval): if parsed.duration is not None: duration = parsed.duration if parsed.start is not None: dt = pendulum.instance(parsed.start, tz=options.get("tz", UTC)) return pendulum.interval( dt, dt.add( years=duration.years, months=duration.months, weeks=duration.weeks, days=duration.remaining_days, hours=duration.hours, minutes=duration.minutes, seconds=duration.remaining_seconds, microseconds=duration.microseconds, ), ) dt = pendulum.instance( t.cast(datetime.datetime, parsed.end), tz=options.get("tz", UTC) ) return pendulum.interval( dt.subtract( years=duration.years, months=duration.months, weeks=duration.weeks, days=duration.remaining_days, hours=duration.hours, minutes=duration.minutes, seconds=duration.remaining_seconds, microseconds=duration.microseconds, ), dt, ) return pendulum.interval( pendulum.instance( t.cast(datetime.datetime, parsed.start), tz=options.get("tz", UTC) ), pendulum.instance( t.cast(datetime.datetime, parsed.end), tz=options.get("tz", UTC) ), ) if isinstance(parsed, Duration): return parsed if RustDuration is not None and isinstance(parsed, RustDuration): return pendulum.duration( years=parsed.years, months=parsed.months, weeks=parsed.weeks, days=parsed.days, hours=parsed.hours, minutes=parsed.minutes, seconds=parsed.seconds, microseconds=parsed.microseconds, ) raise NotImplementedError pendulum-3.0.0/src/pendulum/parsing/000077500000000000000000000000001453741033500174335ustar00rootroot00000000000000pendulum-3.0.0/src/pendulum/parsing/__init__.py000066400000000000000000000136171453741033500215540ustar00rootroot00000000000000from __future__ import annotations import contextlib import copy import os import re import struct from datetime import date from datetime import datetime from datetime import time from typing import Any from typing import Optional from typing import cast from dateutil import parser from pendulum.parsing.exceptions import ParserError with_extensions = os.getenv("PENDULUM_EXTENSIONS", "1") == "1" try: if not with_extensions or struct.calcsize("P") == 4: raise ImportError() from pendulum._pendulum import Duration from pendulum._pendulum import parse_iso8601 except ImportError: from pendulum.duration import Duration # type: ignore[assignment] from pendulum.parsing.iso8601 import parse_iso8601 # type: ignore[assignment] COMMON = re.compile( # Date (optional) # noqa: ERA001 "^" "(?P" " (?P" # Classic date (YYYY-MM-DD) r" (?P\d{4})" # Year " (?P" r" (?P[/:])?(?P\d{2})" # Month (optional) r" ((?P[/:])?(?P\d{2}))" # Day (optional) " )?" " )" ")?" # Time (optional) # noqa: ERA001 "(?P