├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── ---bug-report.md │ ├── ---documentation.md │ ├── ---everything-else.md │ └── ---feature-request.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── codspeed.yml │ ├── release.yml │ └── tests.yml ├── .gitignore ├── .pre-commit-config.yaml ├── CHANGELOG.md ├── LICENSE ├── Makefile ├── README.rst ├── clock ├── docs ├── Makefile ├── docs │ ├── addition_subtraction.md │ ├── attributes_properties.md │ ├── comparison.md │ ├── difference.md │ ├── duration.md │ ├── fluent_helpers.md │ ├── index.md │ ├── installation.md │ ├── instantiation.md │ ├── interval.md │ ├── introduction.md │ ├── limitations.md │ ├── localization.md │ ├── modifiers.md │ ├── parsing.md │ ├── string_formatting.md │ ├── testing.md │ └── timezones.md ├── mkdocs.yml └── theme │ └── main.html ├── poetry.lock ├── pyproject.toml ├── rust ├── .cargo │ └── config.toml ├── Cargo.lock ├── Cargo.toml └── src │ ├── constants.rs │ ├── helpers.rs │ ├── lib.rs │ ├── parsing.rs │ └── python │ ├── helpers.rs │ ├── mod.rs │ ├── parsing.rs │ └── types │ ├── duration.rs │ ├── interval.rs │ ├── mod.rs │ ├── precise_diff.rs │ └── timezone.rs ├── src └── pendulum │ ├── __init__.py │ ├── _helpers.py │ ├── _pendulum.pyi │ ├── constants.py │ ├── date.py │ ├── datetime.py │ ├── day.py │ ├── duration.py │ ├── exceptions.py │ ├── formatting │ ├── __init__.py │ ├── difference_formatter.py │ └── formatter.py │ ├── helpers.py │ ├── interval.py │ ├── locales │ ├── __init__.py │ ├── bg │ │ ├── __init__.py │ │ ├── custom.py │ │ └── locale.py │ ├── cs │ │ ├── __init__.py │ │ ├── custom.py │ │ └── locale.py │ ├── da │ │ ├── __init__.py │ │ ├── custom.py │ │ └── locale.py │ ├── de │ │ ├── __init__.py │ │ ├── custom.py │ │ └── locale.py │ ├── en │ │ ├── __init__.py │ │ ├── custom.py │ │ └── locale.py │ ├── en_gb │ │ ├── __init__.py │ │ ├── custom.py │ │ └── locale.py │ ├── en_us │ │ ├── __init__.py │ │ ├── custom.py │ │ └── locale.py │ ├── es │ │ ├── __init__.py │ │ ├── custom.py │ │ └── locale.py │ ├── fa │ │ ├── __init__.py │ │ ├── custom.py │ │ └── locale.py │ ├── fo │ │ ├── __init__.py │ │ ├── custom.py │ │ └── locale.py │ ├── fr │ │ ├── __init__.py │ │ ├── custom.py │ │ └── locale.py │ ├── he │ │ ├── __init__.py │ │ ├── custom.py │ │ └── locale.py │ ├── id │ │ ├── __init__.py │ │ ├── custom.py │ │ └── locale.py │ ├── it │ │ ├── __init__.py │ │ ├── custom.py │ │ └── locale.py │ ├── ja │ │ ├── __init__.py │ │ ├── custom.py │ │ └── locale.py │ ├── ko │ │ ├── __init__.py │ │ ├── custom.py │ │ └── locale.py │ ├── locale.py │ ├── lt │ │ ├── __init__.py │ │ ├── custom.py │ │ └── locale.py │ ├── nb │ │ ├── __init__.py │ │ ├── custom.py │ │ └── locale.py │ ├── nl │ │ ├── __init__.py │ │ ├── custom.py │ │ └── locale.py │ ├── nn │ │ ├── __init__.py │ │ ├── custom.py │ │ └── locale.py │ ├── pl │ │ ├── __init__.py │ │ ├── custom.py │ │ └── locale.py │ ├── pt_br │ │ ├── __init__.py │ │ ├── custom.py │ │ └── locale.py │ ├── ru │ │ ├── __init__.py │ │ ├── custom.py │ │ └── locale.py │ ├── sk │ │ ├── __init__.py │ │ ├── custom.py │ │ └── locale.py │ ├── sv │ │ ├── __init__.py │ │ ├── custom.py │ │ └── locale.py │ ├── tr │ │ ├── __init__.py │ │ ├── custom.py │ │ └── locale.py │ ├── ua │ │ ├── __init__.py │ │ ├── custom.py │ │ └── locale.py │ └── zh │ │ ├── __init__.py │ │ ├── custom.py │ │ └── locale.py │ ├── mixins │ ├── __init__.py │ └── default.py │ ├── parser.py │ ├── parsing │ ├── __init__.py │ ├── exceptions │ │ └── __init__.py │ └── iso8601.py │ ├── py.typed │ ├── testing │ ├── __init__.py │ └── traveller.py │ ├── time.py │ ├── tz │ ├── __init__.py │ ├── data │ │ ├── __init__.py │ │ └── windows.py │ ├── exceptions.py │ ├── local_timezone.py │ └── timezone.py │ └── utils │ ├── __init__.py │ └── _compat.py ├── tests ├── __init__.py ├── benchmarks │ ├── __init__.py │ └── test_parse_8601.py ├── conftest.py ├── date │ ├── __init__.py │ ├── test_add.py │ ├── test_behavior.py │ ├── test_comparison.py │ ├── test_construct.py │ ├── test_day_of_week_modifiers.py │ ├── test_diff.py │ ├── test_fluent_setters.py │ ├── test_getters.py │ ├── test_start_end_of.py │ ├── test_strings.py │ └── test_sub.py ├── datetime │ ├── __init__.py │ ├── test_add.py │ ├── test_behavior.py │ ├── test_comparison.py │ ├── test_construct.py │ ├── test_create_from_timestamp.py │ ├── test_day_of_week_modifiers.py │ ├── test_diff.py │ ├── test_fluent_setters.py │ ├── test_from_format.py │ ├── test_getters.py │ ├── test_naive.py │ ├── test_replace.py │ ├── test_start_end_of.py │ ├── test_strings.py │ ├── test_sub.py │ └── test_timezone.py ├── duration │ ├── __init__.py │ ├── test_add_sub.py │ ├── test_arithmetic.py │ ├── test_behavior.py │ ├── test_construct.py │ ├── test_in_methods.py │ ├── test_in_words.py │ └── test_total_methods.py ├── fixtures │ ├── __init__.py │ └── tz │ │ ├── Paris │ │ ├── clock │ │ └── etc │ │ │ └── sysconfig │ │ │ └── clock │ │ ├── symlink │ │ ├── etc │ │ │ └── localtime │ │ └── usr │ │ │ └── share │ │ │ └── zoneinfo │ │ │ └── Europe │ │ │ └── Paris │ │ └── timezone_dir │ │ ├── etc │ │ ├── localtime │ │ └── timezone │ │ │ └── blank.md │ │ └── usr │ │ └── share │ │ └── zoneinfo │ │ └── Europe │ │ └── Paris ├── formatting │ ├── __init__.py │ └── test_formatter.py ├── helpers │ ├── __init__.py │ └── test_local_time.py ├── interval │ ├── __init__.py │ ├── test_add_subtract.py │ ├── test_arithmetic.py │ ├── test_behavior.py │ ├── test_construct.py │ ├── test_hashing.py │ ├── test_in_words.py │ └── test_range.py ├── localization │ ├── __init__.py │ ├── test_bg.py │ ├── test_cs.py │ ├── test_da.py │ ├── test_de.py │ ├── test_es.py │ ├── test_fa.py │ ├── test_fo.py │ ├── test_fr.py │ ├── test_he.py │ ├── test_id.py │ ├── test_it.py │ ├── test_ja.py │ ├── test_ko.py │ ├── test_lt.py │ ├── test_nb.py │ ├── test_nl.py │ ├── test_nn.py │ ├── test_pl.py │ ├── test_ru.py │ ├── test_sk.py │ ├── test_sv.py │ └── test_tr.py ├── parsing │ ├── __init__.py │ ├── test_parse_iso8601.py │ ├── test_parsing.py │ └── test_parsing_duration.py ├── test_helpers.py ├── test_main.py ├── test_parsing.py ├── testing │ ├── __init__.py │ └── test_time_travel.py ├── time │ ├── __init__.py │ ├── test_add.py │ ├── test_behavior.py │ ├── test_comparison.py │ ├── test_construct.py │ ├── test_diff.py │ ├── test_fluent_setters.py │ ├── test_strings.py │ └── test_sub.py └── tz │ ├── __init__.py │ ├── test_helpers.py │ ├── test_local_timezone.py │ ├── test_timezone.py │ └── test_timezones.py └── tox.ini /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [sdispater] 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/---bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F41E Bug Report" 3 | about: Did you find a bug? 4 | title: '' 5 | labels: 'Bug' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 17 | 18 | 19 | - [ ] I am on the [latest](https://github.com/sdispater/pendulum/releases/latest) Pendulum version. 20 | - [ ] I have searched the [issues](https://github.com/sdispater/pendulum/issues) of this repo and believe that this is not a duplicate. 21 | 22 | 26 | 27 | - **OS version and name**: 28 | - **Pendulum version**: 29 | 30 | ## Issue 31 | 32 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/---documentation.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F4DA Documentation" 3 | about: Did you find errors, problems, or anything unintelligible in the docs (https://pendulum.eustace.io/docs)? 4 | title: '' 5 | labels: 'Documentation' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 17 | 18 | 19 | - [ ] I have searched the [issues](https://github.com/sdispater/pendulum/issues) of this repo and believe that this is not a duplicate. 20 | 21 | ## Issue 22 | 23 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/---everything-else.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F5C3 Everything Else" 3 | about: For questions and issues that do not fall in any of the other categories. This 4 | can include questions about Pendulum's roadmap. 5 | title: '' 6 | labels: '' 7 | assignees: '' 8 | 9 | --- 10 | 11 | 15 | - [ ] I have searched the [issues](https://github.com/sdispater/pendulum/issues) of this repo and believe that this is not a duplicate. 16 | - [ ] I have searched the [documentation](https://pendulum.eustace.io/docs/) and believe that my question is not covered. 17 | 18 | ## Issue 19 | 20 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/---feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F381 Feature Request" 3 | about: Do you have ideas for new features and improvements? 4 | title: '' 5 | labels: 'Feature' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 17 | 18 | 19 | - [ ] I have searched the [issues](https://github.com/sdispater/pendulum/issues) of this repo and believe that this is not a duplicate. 20 | - [ ] I have searched the [documentation](https://pendulum.eustace.io/docs/) and believe that my question is not covered. 21 | 22 | ## Feature Request 23 | 24 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Pull Request Check List 2 | 3 | 6 | 7 | - [ ] Added **tests** for changed code. 8 | - [ ] Updated **documentation** for changed code. 9 | 10 | 16 | -------------------------------------------------------------------------------- /.github/workflows/codspeed.yml: -------------------------------------------------------------------------------- 1 | name: codspeed 2 | 3 | on: 4 | push: 5 | branches: 6 | - "master" 7 | pull_request: 8 | # `workflow_dispatch` allows CodSpeed to trigger backtest 9 | # performance analysis in order to generate initial data. 10 | workflow_dispatch: 11 | 12 | jobs: 13 | benchmarks: 14 | runs-on: ubuntu-22.04 15 | steps: 16 | - uses: actions/checkout@v3 17 | - uses: actions/setup-python@v4 18 | with: 19 | python-version: "3.9" 20 | 21 | - name: Get full Python version 22 | id: full-python-version 23 | run: | 24 | echo version=$(python -c "import sys; print('-'.join(str(v) for v in sys.version_info))") >> $GITHUB_OUTPUT 25 | 26 | - name: Install poetry 27 | run: | 28 | pipx install poetry>=2 29 | 30 | - name: Configure poetry 31 | run: poetry config virtualenvs.in-project true 32 | 33 | - name: Install dependencies 34 | run: poetry install --only test --only benchmark --only build -vvv --no-root 35 | 36 | - name: Install pendulum and check extensions 37 | run: | 38 | poetry run pip install -e . -vvv 39 | poetry run python -c 'import pendulum._pendulum' 40 | 41 | - name: Run benchmarks 42 | uses: CodSpeedHQ/action@v3 43 | with: 44 | token: ${{ secrets.CODSPEED_TOKEN }} 45 | run: poetry run pytest tests/ --codspeed 46 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: 4 | push: 5 | paths-ignore: 6 | - 'docs/**' 7 | branches: 8 | - master 9 | pull_request: 10 | paths-ignore: 11 | - 'docs/**' 12 | branches: 13 | - '**' 14 | 15 | jobs: 16 | Linting: 17 | name: Linting 18 | runs-on: ubuntu-latest 19 | steps: 20 | - uses: actions/checkout@v3 21 | - uses: actions/setup-python@v4 22 | with: 23 | python-version: "3.11" 24 | - name: "Install pre-commit" 25 | run: pip install pre-commit 26 | - name: "Install Rust toolchain" 27 | run: rustup component add rustfmt clippy 28 | - run: pre-commit run --all-files 29 | 30 | Tests: 31 | name: ${{ matrix.os }} / ${{ matrix.python-version }} 32 | runs-on: ${{ matrix.os }}-latest 33 | strategy: 34 | matrix: 35 | os: [Ubuntu, MacOS, Windows] 36 | python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] 37 | defaults: 38 | run: 39 | shell: bash 40 | 41 | steps: 42 | - uses: actions/checkout@v3 43 | 44 | - name: Set up Python ${{ matrix.python-version }} 45 | uses: actions/setup-python@v4 46 | with: 47 | python-version: ${{ matrix.python-version }} 48 | allow-prereleases: true 49 | 50 | - name: Get full Python version 51 | id: full-python-version 52 | run: | 53 | echo version=$(python -c "import sys; print('-'.join(str(v) for v in sys.version_info))") >> $GITHUB_OUTPUT 54 | 55 | - name: Install poetry 56 | run: | 57 | pipx install poetry>=2 58 | 59 | - name: Configure poetry 60 | run: poetry config virtualenvs.in-project true 61 | 62 | - name: Set up cache 63 | uses: actions/cache@v3 64 | id: cache 65 | with: 66 | path: .venv 67 | key: venv-${{ runner.os }}-${{ steps.full-python-version.outputs.version }}-${{ hashFiles('**/poetry.lock') }} 68 | 69 | - name: Ensure cache is healthy 70 | # MacOS does not come with `timeout` command out of the box 71 | if: steps.cache.outputs.cache-hit == 'true' && matrix.os != 'MacOS' 72 | run: timeout 10s poetry run pip --version || rm -rf .venv 73 | 74 | - name: Install runtime, testing, and typing dependencies 75 | run: poetry install --only main --only test --only typing --only build --no-root -vvv 76 | 77 | - name: Install project 78 | run: poetry run maturin develop 79 | 80 | - name: Run type checking 81 | run: poetry run mypy 82 | 83 | - name: Uninstall typing dependencies 84 | # This ensures pendulum runs without typing_extensions installed 85 | run: poetry sync --only main --only test --only build --no-root -vvv 86 | 87 | - name: Test Pure Python 88 | run: | 89 | PENDULUM_EXTENSIONS=0 poetry run pytest -q tests 90 | 91 | - name: Test 92 | run: | 93 | poetry run pytest -q tests 94 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | 3 | # Packages 4 | *.egg 5 | *.egg-info 6 | dist 7 | build 8 | _build 9 | .cache 10 | *.so 11 | 12 | # Installer logs 13 | pip-log.txt 14 | 15 | # Unit test / coverage reports 16 | .coverage 17 | .tox 18 | nosetests.xml 19 | .pytest_cache 20 | 21 | .DS_Store 22 | .idea/* 23 | .python-version 24 | 25 | /test.py 26 | /test_*.* 27 | /benchmark/* 28 | benchmark.py 29 | results.json 30 | profile.html 31 | /wheelhouse 32 | /docs/site/* 33 | setup.py 34 | 35 | # editor 36 | 37 | .vscode 38 | /target 39 | /rust/target 40 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | ci: 2 | autofix_prs: false 3 | 4 | repos: 5 | - repo: https://github.com/pre-commit/pre-commit-hooks 6 | rev: v4.4.0 7 | hooks: 8 | - id: trailing-whitespace 9 | exclude: ^tests/.*/fixtures/.* 10 | - id: end-of-file-fixer 11 | exclude: ^tests/.*/fixtures/.* 12 | - id: debug-statements 13 | 14 | - repo: https://github.com/astral-sh/ruff-pre-commit 15 | rev: v0.11.2 16 | hooks: 17 | - id: ruff 18 | - id: ruff-format 19 | 20 | - repo: local 21 | hooks: 22 | - id: lint-rust 23 | name: Lint Rust 24 | entry: make lint-rust 25 | types: [rust] 26 | language: rust 27 | pass_filenames: false 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Sébastien Eustace 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | # lists all available targets 3 | list: 4 | @sh -c "$(MAKE) -p no_targets__ | \ 5 | awk -F':' '/^[a-zA-Z0-9][^\$$#\/\\t=]*:([^=]|$$)/ {\ 6 | split(\$$1,A,/ /);for(i in A)print A[i]\ 7 | }' | grep -v '__\$$' | grep -v 'make\[1\]' | grep -v 'Makefile' | sort" 8 | # required for list 9 | no_targets__: 10 | 11 | lint-rust: 12 | cd rust && cargo fmt --all -- --check 13 | cd rust && cargo clippy --tests -- -D warnings 14 | 15 | 16 | format-rust: 17 | cd rust && cargo fmt --all 18 | cd rust && cargo clippy --tests --fix --allow-dirty -- -D warnings 19 | 20 | dev: 21 | poetry install --only main --only test --only typing --only build --only lint 22 | poetry run maturin develop 23 | 24 | lint: 25 | poetry run mypy 26 | poetry run pre-commit run --all-files 27 | 28 | test: 29 | PENDULUM_EXTENSIONS=0 poetry run pytest -q tests 30 | poetry run pytest -q tests 31 | 32 | clean: 33 | rm src/pendulum/*.so 34 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | html: 2 | mkdocs build 3 | -------------------------------------------------------------------------------- /docs/docs/addition_subtraction.md: -------------------------------------------------------------------------------- 1 | # Addition and Subtraction 2 | 3 | To easily add and subtract time, you can use the `add()` and `subtract()` 4 | methods. 5 | Each method returns a new `DateTime` instance. 6 | 7 | ```python 8 | >>> import pendulum 9 | 10 | >>> dt = pendulum.datetime(2012, 1, 31) 11 | 12 | >>> dt.to_datetime_string() 13 | '2012-01-31 00:00:00' 14 | 15 | >>> dt = dt.add(years=5) 16 | '2017-01-31 00:00:00' 17 | >>> dt = dt.add(years=1) 18 | '2018-01-31 00:00:00' 19 | >>> dt = dt.subtract(years=1) 20 | '2017-01-31 00:00:00' 21 | >>> dt = dt.subtract(years=5) 22 | '2012-01-31 00:00:00' 23 | 24 | >>> dt = dt.add(months=60) 25 | '2017-01-31 00:00:00' 26 | >>> dt = dt.add(months=1) 27 | '2017-02-28 00:00:00' 28 | >>> dt = dt.subtract(months=1) 29 | '2017-01-28 00:00:00' 30 | >>> dt = dt.subtract(months=60) 31 | '2012-01-28 00:00:00' 32 | 33 | >>> dt = dt.add(days=29) 34 | '2012-02-26 00:00:00' 35 | >>> dt = dt.add(days=1) 36 | '2012-02-27 00:00:00' 37 | >>> dt = dt.subtract(days=1) 38 | '2012-02-26 00:00:00' 39 | >>> dt = dt.subtract(days=29) 40 | '2012-01-28 00:00:00' 41 | 42 | >>> dt = dt.add(weeks=3) 43 | '2012-02-18 00:00:00' 44 | >>> dt = dt.add(weeks=1) 45 | '2012-02-25 00:00:00' 46 | >>> dt = dt.subtract(weeks=1) 47 | '2012-02-18 00:00:00' 48 | >>> dt = dt.subtract(weeks=3) 49 | '2012-01-28 00:00:00' 50 | 51 | >>> dt = dt.add(hours=24) 52 | '2012-01-29 00:00:00' 53 | >>> dt = dt.add(hours=1) 54 | '2012-02-25 01:00:00' 55 | >>> dt = dt.subtract(hours=1) 56 | '2012-02-29 00:00:00' 57 | >>> dt = dt.subtract(hours=24) 58 | '2012-01-28 00:00:00' 59 | 60 | >>> dt = dt.add(minutes=61) 61 | '2012-01-28 01:01:00' 62 | >>> dt = dt.add(minutes=1) 63 | '2012-01-28 01:02:00' 64 | >>> dt = dt.subtract(minutes=1) 65 | '2012-01-28 01:01:00' 66 | >>> dt = dt.subtract(minutes=24) 67 | '2012-01-28 00:00:00' 68 | 69 | >>> dt = dt.add(seconds=61) 70 | '2012-01-28 00:01:01' 71 | >>> dt = dt.add(seconds=1) 72 | '2012-01-28 00:01:02' 73 | >>> dt = dt.subtract(seconds=1) 74 | '2012-01-28 00:01:01' 75 | >>> dt = dt.subtract(seconds=61) 76 | '2012-01-28 00:00:00' 77 | 78 | >>> dt = dt.add(years=3, months=2, days=6, hours=12, minutes=31, seconds=43) 79 | '2015-04-03 12:31:43' 80 | >>> dt = dt.subtract(years=3, months=2, days=6, hours=12, minutes=31, seconds=43) 81 | '2012-01-28 00:00:00' 82 | ``` 83 | 84 | !!!note 85 | 86 | Passing negative values to `add()` is also possible and will act exactly 87 | like `subtract()` 88 | -------------------------------------------------------------------------------- /docs/docs/attributes_properties.md: -------------------------------------------------------------------------------- 1 | # Attributes and Properties 2 | 3 | Pendulum gives access to more attributes and properties than the default ``datetime`` class. 4 | 5 | ```python 6 | >>> import pendulum 7 | 8 | >>> dt = pendulum.parse('2012-09-05T23:26:11.123789') 9 | 10 | # These properties specifically return integers 11 | >>> dt.year 12 | 2012 13 | >>> dt.month 14 | 9 15 | >>> dt.day 16 | 5 17 | >>> dt.hour 18 | 23 19 | >>> dt.minute 20 | 26 21 | >>> dt.second 22 | 11 23 | >>> dt.microsecond 24 | 123789 25 | >>> dt.day_of_week 26 | 3 27 | >>> dt.day_of_year 28 | 248 29 | >>> dt.week_of_month 30 | 1 31 | >>> dt.week_of_year 32 | 36 33 | >>> dt.days_in_month 34 | 30 35 | >>> dt.timestamp() 36 | 1346887571.123789 37 | >>> dt.float_timestamp 38 | 1346887571.123789 39 | >>> dt.int_timestamp 40 | 1346887571 41 | 42 | >>> pendulum.datetime(1975, 5, 21).age 43 | 41 # calculated vs now in the same tz 44 | >>> dt.quarter 45 | 3 46 | 47 | # Returns an int of seconds difference from UTC (+/- sign included) 48 | >>> pendulum.from_timestamp(0).offset 49 | 0 50 | >>> pendulum.from_timestamp(0, 'America/Toronto').offset 51 | -18000 52 | 53 | # Returns a float of hours difference from UTC (+/- sign included) 54 | >>> pendulum.from_timestamp(0, 'America/Toronto').offset_hours 55 | -5.0 56 | >>> pendulum.from_timestamp(0, 'Australia/Adelaide').offset_hours 57 | 9.5 58 | 59 | # Gets the timezone instance 60 | >>> pendulum.now().timezone 61 | >>> pendulum.now().tz 62 | 63 | # Gets the timezone name 64 | >>> pendulum.now().timezone_name 65 | 66 | # Indicates if daylight savings time is on 67 | >>> dt = pendulum.datetime(2012, 1, 1, tz='America/Toronto') 68 | >>> dt.is_dst() 69 | False 70 | >>> dt = pendulum.datetime(2012, 9, 1, tz='America/Toronto') 71 | >>> dt.is_dst() 72 | True 73 | 74 | # Indicates if the instance is in the same timezone as the local timezone 75 | >>> pendulum.now().is_local() 76 | True 77 | >>> pendulum.now('Europe/London').is_local() 78 | False 79 | 80 | # Indicates if the instance is in the UTC timezone 81 | >>> pendulum.now().is_utc() 82 | False 83 | >>> pendulum.now('Europe/London').is_local() 84 | False 85 | >>> pendulum.now('UTC').is_utc() 86 | True 87 | ``` 88 | -------------------------------------------------------------------------------- /docs/docs/comparison.md: -------------------------------------------------------------------------------- 1 | # Comparison 2 | 3 | Simple comparison is offered up via the basic operators. 4 | Remember that the comparison is done in the UTC timezone 5 | so things aren't always as they seem. 6 | 7 | ```python 8 | >>> import pendulum 9 | 10 | >>> first = pendulum.datetime(2012, 9, 5, 23, 26, 11, 0, tz='America/Toronto') 11 | >>> second = pendulum.datetime(2012, 9, 5, 20, 26, 11, 0, tz='America/Vancouver') 12 | 13 | >>> first.to_datetime_string() 14 | '2012-09-05 23:26:11' 15 | >>> first.timezone_name 16 | 'America/Toronto' 17 | >>> second.to_datetime_string() 18 | '2012-09-05 20:26:11' 19 | >>> second.timezone_name 20 | 'America/Vancouver' 21 | 22 | >>> first == second 23 | True 24 | >>> first != second 25 | False 26 | >>> first > second 27 | False 28 | >>> first >= second 29 | True 30 | >>> first < second 31 | False 32 | >>> first <= second 33 | True 34 | 35 | >>> first = first.on(2012, 1, 1).at(0, 0, 0) 36 | >>> second = second.on(2012, 1, 1).at(0, 0, 0) 37 | # tz is still America/Vancouver for second 38 | 39 | >>> first == second 40 | False 41 | >>> first != second 42 | True 43 | >>> first > second 44 | False 45 | >>> first >= second 46 | False 47 | >>> first < second 48 | True 49 | >>> first <= second 50 | True 51 | ``` 52 | 53 | To handle the most used cases there are some simple helper functions. 54 | For the methods that compare to `now()` (ex. `is_today()`) in some manner 55 | the `now()` is created in the same timezone as the instance. 56 | 57 | ```python 58 | >>> import pendulum 59 | 60 | >>> dt = pendulum.now() 61 | 62 | >>> dt.is_past() 63 | >>> dt.is_leap_year() 64 | 65 | >>> born = pendulum.datetime(1987, 4, 23) 66 | >>> not_birthday = pendulum.datetime(2014, 9, 26) 67 | >>> birthday = pendulum.datetime(2014, 4, 23) 68 | >>> past_birthday = pendulum.now().subtract(years=50) 69 | 70 | >>> born.is_birthday(not_birthday) 71 | False 72 | >>> born.is_birthday(birthday) 73 | True 74 | >>> past_birthday.is_birthday() 75 | # Compares to now by default 76 | True 77 | ``` 78 | -------------------------------------------------------------------------------- /docs/docs/fluent_helpers.md: -------------------------------------------------------------------------------- 1 | # Fluent helpers 2 | 3 | Pendulum provides helpers that return a new instance with some attributes 4 | modified compared to the original instance. 5 | However, none of these helpers, with the exception of explicitly setting the 6 | timezone, will change the timezone of the instance. Specifically, 7 | setting the timestamp will not set the corresponding timezone to UTC. 8 | 9 | ```python 10 | >>> import pendulum 11 | 12 | >>> dt = pendulum.now() 13 | 14 | >>> dt.set(year=1975, month=5, day=21).to_datetime_string() 15 | '1975-05-21 13:45:18' 16 | 17 | >>> dt.set(hour=22, minute=32, second=5).to_datetime_string() 18 | '2016-11-16 22:32:05' 19 | ``` 20 | 21 | You can also use the `on()` and `at()` methods to change the date and the time 22 | respectively 23 | 24 | ```python 25 | >>> dt.on(1975, 5, 21).at(22, 32, 5).to_datetime_string() 26 | '1975-05-21 22:32:05' 27 | 28 | >>> dt.at(10).to_datetime_string() 29 | '2016-11-16 10:00:00' 30 | 31 | >>> dt.at(10, 30).to_datetime_string() 32 | '2016-11-16 10:30:00' 33 | ``` 34 | 35 | You can also modify the timezone. 36 | 37 | ```python 38 | >>> dt.set(tz='Europe/London') 39 | ``` 40 | 41 | Setting the timezone just modifies the timezone information without 42 | making any conversion, while `in_timezone()` (or `in_tz()`) 43 | converts the time in the appropriate timezone. 44 | 45 | ```python 46 | >>> import pendulum 47 | 48 | >>> dt = pendulum.datetime(2013, 3, 31, 2, 30) 49 | >>> print(dt) 50 | '2013-03-31T02:30:00+00:00' 51 | 52 | >>> dt = dt.set(tz='Europe/Paris') 53 | >>> print(dt) 54 | '2013-03-31T03:30:00+02:00' 55 | 56 | >>> dt = dt.in_tz('Europe/Paris') 57 | >>> print(dt) 58 | '2013-03-31T04:30:00+02:00' 59 | 60 | >>> dt = dt.set(tz='Europe/Paris').set(tz='UTC') 61 | >>> print(dt) 62 | '2013-03-31T03:30:00+00:00' 63 | 64 | >>> dt = dt.in_tz('Europe/Paris').in_tz('UTC') 65 | >>> print(dt) 66 | '2013-03-31T02:30:00+00:00' 67 | ``` 68 | -------------------------------------------------------------------------------- /docs/docs/index.md: -------------------------------------------------------------------------------- 1 | {!docs/installation.md!} 2 | {!docs/introduction.md!} 3 | {!docs/instantiation.md!} 4 | {!docs/parsing.md!} 5 | {!docs/localization.md!} 6 | {!docs/attributes_properties.md!} 7 | {!docs/fluent_helpers.md!} 8 | {!docs/string_formatting.md!} 9 | {!docs/comparison.md!} 10 | {!docs/addition_subtraction.md!} 11 | {!docs/difference.md!} 12 | {!docs/modifiers.md!} 13 | {!docs/timezones.md!} 14 | {!docs/duration.md!} 15 | {!docs/interval.md!} 16 | {!docs/testing.md!} 17 | {!docs/limitations.md!} 18 | -------------------------------------------------------------------------------- /docs/docs/installation.md: -------------------------------------------------------------------------------- 1 | # Installation 2 | 3 | Installing `pendulum` is quite simple: 4 | 5 | ```bash 6 | $ pip install pendulum 7 | ``` 8 | 9 | or, if you are using [poetry](https://python-poetry.org): 10 | 11 | ```bash 12 | $ poetry add pendulum 13 | ``` 14 | 15 | ## Optional features 16 | 17 | Pendulum provides optional features that you must explicitly require in order to use them. 18 | 19 | These optional features are: 20 | 21 | - `test`: Provides a set of helpers to make testing easier by allowing you to control the flow of time. 22 | 23 | You can install them by specifying them when installing Pendulum 24 | 25 | ```bash 26 | $ pip install pendulum[test] 27 | ``` 28 | 29 | or, if you are using [poetry](https://python-poetry.org): 30 | 31 | ```bash 32 | $ poetry add pendulum[test] 33 | ``` 34 | -------------------------------------------------------------------------------- /docs/docs/introduction.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | Pendulum is a Python package to ease datetimes manipulation. 4 | 5 | It provides classes that are drop-in replacements for the native ones (they inherit from them). 6 | 7 | Special care has been taken to ensure timezones are handled correctly, 8 | and are based on the underlying `tzinfo` implementation. 9 | For example, all comparisons are done in `UTC` or in the timezone of the datetime being used. 10 | 11 | ```python 12 | >>> import pendulum 13 | 14 | >>> dt_toronto = pendulum.datetime(2012, 1, 1, tz='America/Toronto') 15 | >>> dt_vancouver = pendulum.datetime(2012, 1, 1, tz='America/Vancouver') 16 | 17 | >>> print(dt_vancouver.diff(dt_toronto).in_hours()) 18 | 3 19 | ``` 20 | 21 | The default timezone, except when using the `now()` method, will always be `UTC`. 22 | -------------------------------------------------------------------------------- /docs/docs/limitations.md: -------------------------------------------------------------------------------- 1 | # Limitations 2 | 3 | Even though the `DateTime` class is a subclass of `datetime`, 4 | there are some rare cases where it can't replace the native class directly. 5 | Here is a list (non-exhaustive) of the reported cases with a possible solution, if any: 6 | 7 | * `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: 8 | 9 | ```python 10 | import pendulum 11 | from sqlite3 import register_adapter 12 | 13 | register_adapter(pendulum.DateTime, lambda val: val.isoformat(' ')) 14 | ``` 15 | 16 | * `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: 17 | 18 | ```python 19 | import pendulum 20 | import MySQLdb.converters 21 | import pymysql.converters 22 | 23 | MySQLdb.converters.conversions[pendulum.DateTime] = MySQLdb.converters.DateTime2literal 24 | pymysql.converters.conversions[pendulum.DateTime] = pymysql.converters.escape_datetime 25 | ``` 26 | 27 | * `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`: 28 | 29 | ```python 30 | import pendulum 31 | from django.db.models import DateTimeField as BaseDateTimeField 32 | 33 | 34 | class DateTimeField(BaseDateTimeField): 35 | 36 | def value_to_string(self, obj): 37 | val = self.value_from_object(obj) 38 | 39 | if isinstance(value, pendulum.DateTime): 40 | return value.format('YYYY-MM-DD HH:mm:ss') 41 | 42 | return '' if val is None else val.isoformat() 43 | ``` 44 | -------------------------------------------------------------------------------- /docs/docs/localization.md: -------------------------------------------------------------------------------- 1 | # Localization 2 | 3 | Localization occurs when using the `format()` method which accepts a `locale` keyword. 4 | 5 | ```python 6 | >>> import pendulum 7 | 8 | >>> dt = pendulum.datetime(1975, 5, 21) 9 | >>> dt.format('dddd DD MMMM YYYY', locale='de') 10 | 'Mittwoch 21 Mai 1975' 11 | 12 | >>> dt.format('dddd DD MMMM YYYY') 13 | 'Wednesday 21 May 1975' 14 | ``` 15 | 16 | `diff_for_humans()` is also localized, you can set the locale 17 | by using `pendulum.set_locale()`. 18 | 19 | ```python 20 | >>> import pendulum 21 | 22 | >>> pendulum.set_locale('de') 23 | >>> pendulum.now().add(years=1).diff_for_humans() 24 | 'in 1 Jahr' 25 | >>> pendulum.set_locale('en') 26 | ``` 27 | 28 | However, you might not want to set the locale globally. The `diff_for_humans()` 29 | method accepts a `locale` keyword argument to use a locale for a specific call. 30 | 31 | ```python 32 | >>> pendulum.set_locale('de') 33 | >>> dt = pendulum.now().add(years=1) 34 | >>> dt.diff_for_humans(locale='fr') 35 | 'dans 1 an' 36 | ``` 37 | -------------------------------------------------------------------------------- /docs/docs/modifiers.md: -------------------------------------------------------------------------------- 1 | # Modifiers 2 | 3 | This group of methods performs helpful modifications to a copy of the current instance. 4 | You'll notice that the `start_of()`, `next()` and `previous()` methods 5 | set the time to `00:00:00` and the `end_of()` methods set the time to `23:59:59.999999`. 6 | 7 | The only one slightly different is the `average()` method. 8 | It returns the middle date between itself and the provided `DateTime` argument. 9 | 10 | ```python 11 | >>> import pendulum 12 | 13 | >>> dt = pendulum.datetime(2012, 1, 31, 12, 0, 0) 14 | 15 | >>> dt.start_of('day') 16 | '2012-01-31 00:00:00' 17 | 18 | >>> dt.end_of('day') 19 | '2012-01-31 23:59:59' 20 | 21 | >>> dt.start_of('month') 22 | '2012-01-01 00:00:00' 23 | 24 | >>> dt.end_of('month') 25 | '2012-01-31 23:59:59' 26 | 27 | >>> dt.start_of('year') 28 | '2012-01-01 00:00:00' 29 | 30 | >>> dt.end_of('year') 31 | '2012-12-31 23:59:59' 32 | 33 | >>> dt.start_of('decade') 34 | '2010-01-01 00:00:00' 35 | 36 | >>> dt.end_of('decade') 37 | '2019-12-31 23:59:59' 38 | 39 | >>> dt.start_of('century') 40 | '2000-01-01 00:00:00' 41 | 42 | >>> dt.end_of('century') 43 | '2099-12-31 23:59:59' 44 | 45 | >>> dt.start_of('week') 46 | '2012-01-30 00:00:00' 47 | >>> dt.day_of_week == pendulum.MONDAY 48 | True # ISO8601 week starts on Monday 49 | 50 | >>> dt.end_of('week') 51 | '2012-02-05 23:59:59' 52 | >>> dt.day_of_week == pendulum.SUNDAY 53 | True # ISO8601 week ends on SUNDAY 54 | 55 | >>> dt.next(pendulum.WEDNESDAY) 56 | '2012-02-01 00:00:00' 57 | >>> dt.day_of_week == pendulum.WEDNESDAY 58 | True 59 | 60 | >>> dt = pendulum.datetime(2012, 1, 1, 12, 0, 0) 61 | dt.next() 62 | '2012-01-08 00:00:00' 63 | >>> dt.next(keep_time=True) 64 | '2012-01-08T12:00:00+00:00' 65 | 66 | >>> dt = pendulum.datetime(2012, 1, 31, 12, 0, 0) 67 | >>> dt.previous(pendulum.WEDNESDAY) 68 | '2012-01-25 00:00:00' 69 | >>> dt.day_of_week == pendulum.WEDNESDAY 70 | True 71 | 72 | >>> dt = pendulum.datetime(2012, 1, 1, 12, 0, 0) 73 | >>> dt.previous() 74 | '2011-12-25 00:00:00' 75 | >>> dt.previous(keep_time=True) 76 | '2011-12-25 12:00:00' 77 | 78 | >>> start = pendulum.datetime(2014, 1, 1) 79 | >>> end = pendulum.datetime(2014, 1, 30) 80 | >>> start.average(end) 81 | '2014-01-15 12:00:00' 82 | 83 | # others that are defined that are similar 84 | # and that accept month, quarter and year units 85 | # first_of(), last_of(), nth_of() 86 | ``` 87 | -------------------------------------------------------------------------------- /docs/docs/testing.md: -------------------------------------------------------------------------------- 1 | # Testing 2 | 3 | Pendulum provides a few helpers to help you control the flow of time in your tests. Note that 4 | these helpers are only available if you opted in the `test` extra during [installation](#installation). 5 | 6 | !!!warning 7 | If you are migrating from Pendulum 2, note that the `set_test_now()` and `test()` 8 | helpers have been removed. 9 | 10 | 11 | ## Relative time travel 12 | 13 | You can travel in time relatively to the current time 14 | 15 | ```python 16 | >>> import pendulum 17 | 18 | >>> now = pendulum.now() 19 | >>> pendulum.travel(minutes=5) 20 | >>> pendulum.now().diff_for_humans(now) 21 | "5 minutes after" 22 | ``` 23 | 24 | Note that once you've travelled in time the clock **keeps ticking**. If you prefer to stop the time completely 25 | you can use the `freeze` parameter: 26 | 27 | ```python 28 | >>> import pendulum 29 | 30 | >>> now = pendulum.now() 31 | >>> pendulum.travel(minutes=5, freeze=True) 32 | >>> pendulum.now().diff_for_humans(now) 33 | "5 minutes after" # This will stay like this indefinitely 34 | ``` 35 | 36 | 37 | ## Absolute time travel 38 | 39 | Sometimes, you may want to place yourself at a specific point in time. 40 | This is possible by using the `travel_to()` helper. This helper accepts a `DateTime` instance 41 | that represents the point in time where you want to travel to. 42 | 43 | ```python 44 | >>> import pendulum 45 | 46 | >>> pendulum.travel_to(pendulum.yesterday()) 47 | ``` 48 | 49 | Similarly to `travel`, it's important to remember that, by default, the time keeps ticking so, if you prefer 50 | stopping the time, use the `freeze` parameter: 51 | 52 | ```python 53 | >>> import pendulum 54 | 55 | >>> pendulum.travel_to(pendulum.yesterday(), freeze=True) 56 | ``` 57 | 58 | ## Travelling back to the present 59 | 60 | Using any of the travel helpers will keep you in the past, or future, until you decide 61 | to travel back to the present time. To do so, you may use the `travel_back()` helper. 62 | 63 | ```python 64 | >>> import pendulum 65 | 66 | >>> now = pendulum.now() 67 | >>> pendulum.travel(minutes=5, freeze=True) 68 | >>> pendulum.now().diff_for_humans(now) 69 | "5 minutes after" 70 | >>> pendulum.travel_back() 71 | >>> pendulum.now().diff_for_humans(now) 72 | "a few seconds after" 73 | ``` 74 | 75 | However, it might be cumbersome to remember to travel back so, instead, you can use any of the helpers as a context 76 | manager: 77 | 78 | ```python 79 | >>> import pendulum 80 | 81 | >>> now = pendulum.now() 82 | >>> with pendulum.travel(minutes=5, freeze=True): 83 | >>> pendulum.now().diff_for_humans(now) 84 | "5 minutes after" 85 | >>> pendulum.now().diff_for_humans(now) 86 | "a few seconds after" 87 | ``` 88 | -------------------------------------------------------------------------------- /docs/mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: Pendulum documentation 2 | 3 | theme: 4 | name: null 5 | custom_dir: theme 6 | 7 | extra: 8 | version: 2.1 9 | 10 | markdown_extensions: 11 | - codehilite 12 | - admonition 13 | - pymdownx.superfences 14 | - toc: 15 | permalink:  16 | - markdown_include.include: 17 | base_path: docs 18 | -------------------------------------------------------------------------------- /docs/theme/main.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: documentation 3 | title: {{ config.site_name|striptags|e }} 4 | version: 2.x 5 | --- 6 | 7 | 8 | 9 |
10 |
11 |
12 |
13 |
Version {{config.extra.version}}
14 |

Documentation

15 |
16 |
17 |
18 |
19 | 20 | 21 |
22 |
23 |
24 |
25 |
26 |
27 | {{page.content}} 28 |
29 |
30 |
31 |
32 |
33 |
34 | -------------------------------------------------------------------------------- /rust/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | rustflags = [] 3 | 4 | # see https://pyo3.rs/main/building_and_distribution.html#macos 5 | [target.x86_64-apple-darwin] 6 | rustflags = [ 7 | "-C", "link-arg=-undefined", 8 | "-C", "link-arg=dynamic_lookup", 9 | ] 10 | 11 | [target.aarch64-apple-darwin] 12 | rustflags = [ 13 | "-C", "link-arg=-undefined", 14 | "-C", "link-arg=dynamic_lookup", 15 | ] 16 | -------------------------------------------------------------------------------- /rust/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "_pendulum" 3 | version = "3.2.0-dev0" 4 | edition = "2021" 5 | 6 | [lib] 7 | name = "_pendulum" 8 | crate-type = ["cdylib"] 9 | 10 | [profile.release] 11 | lto = "fat" 12 | codegen-units = 1 13 | strip = true 14 | overflow-checks = false 15 | 16 | [dependencies] 17 | pyo3 = { version = "0.24", features = ["extension-module", "generate-import-lib"] } 18 | 19 | [features] 20 | extension-module = ["pyo3/extension-module"] 21 | -------------------------------------------------------------------------------- /rust/src/constants.rs: -------------------------------------------------------------------------------- 1 | pub const EPOCH_YEAR: u32 = 1970; 2 | 3 | pub const DAYS_PER_N_YEAR: u32 = 365; 4 | pub const DAYS_PER_L_YEAR: u32 = 366; 5 | 6 | pub const SECS_PER_MIN: u32 = 60; 7 | pub const SECS_PER_HOUR: u32 = SECS_PER_MIN * 60; 8 | pub const SECS_PER_DAY: u32 = SECS_PER_HOUR * 24; 9 | 10 | // 400-year chunks always have 146097 days (20871 weeks). 11 | pub const DAYS_PER_400_YEARS: u32 = 146_097; 12 | pub const SECS_PER_400_YEARS: u64 = DAYS_PER_400_YEARS as u64 * SECS_PER_DAY as u64; 13 | 14 | // The number of seconds in an aligned 100-year chunk, for those that 15 | // do not begin with a leap year and those that do respectively. 16 | pub const SECS_PER_100_YEARS: [u64; 2] = [ 17 | (76 * DAYS_PER_N_YEAR as u64 + 24 * DAYS_PER_L_YEAR as u64) * SECS_PER_DAY as u64, 18 | (75 * DAYS_PER_N_YEAR as u64 + 25 * DAYS_PER_L_YEAR as u64) * SECS_PER_DAY as u64, 19 | ]; 20 | 21 | // The number of seconds in an aligned 4-year chunk, for those that 22 | // do not begin with a leap year and those that do respectively. 23 | #[allow(clippy::erasing_op)] 24 | pub const SECS_PER_4_YEARS: [u32; 2] = [ 25 | (4 * DAYS_PER_N_YEAR + 0 * DAYS_PER_L_YEAR) * SECS_PER_DAY, 26 | (3 * DAYS_PER_N_YEAR + DAYS_PER_L_YEAR) * SECS_PER_DAY, 27 | ]; 28 | 29 | // The number of seconds in non-leap and leap years respectively. 30 | pub const SECS_PER_YEAR: [u32; 2] = [ 31 | DAYS_PER_N_YEAR * SECS_PER_DAY, 32 | DAYS_PER_L_YEAR * SECS_PER_DAY, 33 | ]; 34 | 35 | // The month lengths in non-leap and leap years respectively. 36 | pub const DAYS_PER_MONTHS: [[i32; 13]; 2] = [ 37 | [-1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31], 38 | [-1, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31], 39 | ]; 40 | 41 | // The day offsets of the beginning of each (1-based) month in non-leap 42 | // and leap years respectively. 43 | // For example, in a leap year there are 335 days before December. 44 | pub const MONTHS_OFFSETS: [[i32; 14]; 2] = [ 45 | [ 46 | -1, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365, 47 | ], 48 | [ 49 | -1, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366, 50 | ], 51 | ]; 52 | 53 | pub const DAY_OF_WEEK_TABLE: [u32; 12] = [0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4]; 54 | 55 | pub const TM_JANUARY: usize = 0; 56 | pub const TM_DECEMBER: usize = 11; 57 | -------------------------------------------------------------------------------- /rust/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate core; 2 | 3 | mod constants; 4 | mod helpers; 5 | mod parsing; 6 | mod python; 7 | 8 | pub use python::_pendulum; 9 | -------------------------------------------------------------------------------- /rust/src/python/mod.rs: -------------------------------------------------------------------------------- 1 | use pyo3::prelude::*; 2 | 3 | mod helpers; 4 | mod parsing; 5 | mod types; 6 | 7 | use helpers::{days_in_year, is_leap, is_long_year, local_time, precise_diff, week_day}; 8 | use parsing::parse_iso8601; 9 | use types::{Duration, PreciseDiff}; 10 | 11 | #[pymodule] 12 | pub fn _pendulum(_py: Python<'_>, m: &Bound) -> PyResult<()> { 13 | m.add_function(wrap_pyfunction!(days_in_year, m)?)?; 14 | m.add_function(wrap_pyfunction!(is_leap, m)?)?; 15 | m.add_function(wrap_pyfunction!(is_long_year, m)?)?; 16 | m.add_function(wrap_pyfunction!(local_time, m)?)?; 17 | m.add_function(wrap_pyfunction!(week_day, m)?)?; 18 | m.add_function(wrap_pyfunction!(parse_iso8601, m)?)?; 19 | m.add_function(wrap_pyfunction!(precise_diff, m)?)?; 20 | m.add_class::()?; 21 | m.add_class::()?; 22 | 23 | Ok(()) 24 | } 25 | -------------------------------------------------------------------------------- /rust/src/python/types/duration.rs: -------------------------------------------------------------------------------- 1 | use pyo3::prelude::*; 2 | 3 | #[pyclass(module = "_pendulum")] 4 | pub struct Duration { 5 | #[pyo3(get, set)] 6 | pub years: u32, 7 | #[pyo3(get, set)] 8 | pub months: u32, 9 | #[pyo3(get, set)] 10 | pub weeks: u32, 11 | #[pyo3(get, set)] 12 | pub days: u32, 13 | #[pyo3(get, set)] 14 | pub hours: u32, 15 | #[pyo3(get, set)] 16 | pub minutes: u32, 17 | #[pyo3(get, set)] 18 | pub seconds: u32, 19 | #[pyo3(get, set)] 20 | pub microseconds: u32, 21 | } 22 | 23 | #[pymethods] 24 | impl Duration { 25 | #[new] 26 | #[pyo3(signature = (years=0, months=0, weeks=0, days=0, hours=0, minutes=0, seconds=0, microseconds=0))] 27 | #[allow(clippy::too_many_arguments)] 28 | pub fn new( 29 | years: Option, 30 | months: Option, 31 | weeks: Option, 32 | days: Option, 33 | hours: Option, 34 | minutes: Option, 35 | seconds: Option, 36 | microseconds: Option, 37 | ) -> Self { 38 | Self { 39 | years: years.unwrap_or(0), 40 | months: months.unwrap_or(0), 41 | weeks: weeks.unwrap_or(0), 42 | days: days.unwrap_or(0), 43 | hours: hours.unwrap_or(0), 44 | minutes: minutes.unwrap_or(0), 45 | seconds: seconds.unwrap_or(0), 46 | microseconds: microseconds.unwrap_or(0), 47 | } 48 | } 49 | 50 | #[getter] 51 | fn remaining_days(&self) -> PyResult { 52 | Ok(self.days) 53 | } 54 | 55 | #[getter] 56 | fn remaining_seconds(&self) -> PyResult { 57 | Ok(self.seconds) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /rust/src/python/types/interval.rs: -------------------------------------------------------------------------------- 1 | use pyo3::prelude::*; 2 | 3 | use pyo3::types::PyDelta; 4 | 5 | #[pyclass(extends=PyDelta)] 6 | #[derive(Default)] 7 | pub struct Interval { 8 | pub days: i32, 9 | pub seconds: i32, 10 | pub microseconds: i32, 11 | } 12 | 13 | #[pymethods] 14 | impl Interval { 15 | #[new] 16 | #[pyo3(signature = (days=0, seconds=0, microseconds=0, milliseconds=0, minutes=0, hours=0, weeks=0))] 17 | pub fn new( 18 | py: Python, 19 | days: Option, 20 | seconds: Option, 21 | microseconds: Option, 22 | milliseconds: Option, 23 | minutes: Option, 24 | hours: Option, 25 | weeks: Option, 26 | ) -> PyResult { 27 | println!("{} days", 31); 28 | PyDelta::new( 29 | py, 30 | days.unwrap_or(0), 31 | seconds.unwrap_or(0), 32 | microseconds.unwrap_or(0), 33 | true, 34 | )?; 35 | 36 | let f = Ok(Self { 37 | days: days.unwrap_or(0), 38 | seconds: seconds.unwrap_or(0), 39 | microseconds: microseconds.unwrap_or(0), 40 | }); 41 | 42 | println!("{} days", 31); 43 | 44 | f 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /rust/src/python/types/mod.rs: -------------------------------------------------------------------------------- 1 | mod duration; 2 | mod precise_diff; 3 | mod timezone; 4 | 5 | pub use duration::Duration; 6 | pub use precise_diff::PreciseDiff; 7 | pub use timezone::FixedTimezone; 8 | -------------------------------------------------------------------------------- /rust/src/python/types/precise_diff.rs: -------------------------------------------------------------------------------- 1 | use pyo3::prelude::*; 2 | 3 | #[pyclass(module = "_pendulum")] 4 | pub struct PreciseDiff { 5 | #[pyo3(get, set)] 6 | pub years: i32, 7 | #[pyo3(get, set)] 8 | pub months: i32, 9 | #[pyo3(get, set)] 10 | pub days: i32, 11 | #[pyo3(get, set)] 12 | pub hours: i32, 13 | #[pyo3(get, set)] 14 | pub minutes: i32, 15 | #[pyo3(get, set)] 16 | pub seconds: i32, 17 | #[pyo3(get, set)] 18 | pub microseconds: i32, 19 | #[pyo3(get, set)] 20 | pub total_days: i32, 21 | } 22 | 23 | #[pymethods] 24 | impl PreciseDiff { 25 | #[new] 26 | #[pyo3(signature = (years=0, months=0, days=0, hours=0, minutes=0, seconds=0, microseconds=0, total_days=0))] 27 | #[allow(clippy::too_many_arguments)] 28 | pub fn new( 29 | years: Option, 30 | months: Option, 31 | days: Option, 32 | hours: Option, 33 | minutes: Option, 34 | seconds: Option, 35 | microseconds: Option, 36 | total_days: Option, 37 | ) -> Self { 38 | Self { 39 | years: years.unwrap_or(0), 40 | months: months.unwrap_or(0), 41 | days: days.unwrap_or(0), 42 | hours: hours.unwrap_or(0), 43 | minutes: minutes.unwrap_or(0), 44 | seconds: seconds.unwrap_or(0), 45 | microseconds: microseconds.unwrap_or(0), 46 | total_days: total_days.unwrap_or(0), 47 | } 48 | } 49 | 50 | fn __repr__(&self) -> String { 51 | 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) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /rust/src/python/types/timezone.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::useless_conversion)] 2 | use pyo3::prelude::*; 3 | use pyo3::types::{PyDelta, PyDict, PyTzInfo}; 4 | 5 | #[pyclass(module = "_pendulum", extends = PyTzInfo)] 6 | #[derive(Clone)] 7 | pub struct FixedTimezone { 8 | offset: i32, 9 | name: Option, 10 | } 11 | 12 | #[pymethods] 13 | impl FixedTimezone { 14 | #[new] 15 | #[pyo3(signature = (offset, name=None))] 16 | pub fn new(offset: i32, name: Option) -> Self { 17 | Self { offset, name } 18 | } 19 | 20 | fn utcoffset<'p>( 21 | &self, 22 | py: Python<'p>, 23 | _dt: &Bound<'p, PyAny>, 24 | ) -> Result, PyErr> { 25 | PyDelta::new(py, 0, self.offset, 0, true) 26 | } 27 | 28 | fn tzname(&self, _dt: &Bound) -> String { 29 | self.__str__() 30 | } 31 | 32 | fn dst<'p>( 33 | &self, 34 | py: Python<'p>, 35 | _dt: &Bound<'p, PyAny>, 36 | ) -> Result, PyErr> { 37 | PyDelta::new(py, 0, 0, 0, true) 38 | } 39 | 40 | fn __repr__(&self) -> String { 41 | format!( 42 | "FixedTimezone({}, name=\"{}\")", 43 | self.offset, 44 | self.__str__() 45 | ) 46 | } 47 | 48 | fn __str__(&self) -> String { 49 | if let Some(n) = &self.name { 50 | n.clone() 51 | } else { 52 | let sign = if self.offset < 0 { "-" } else { "+" }; 53 | let minutes = self.offset.abs() / 60; 54 | let (hour, minute) = (minutes / 60, minutes % 60); 55 | format!("{sign}{hour:.2}:{minute:.2}") 56 | } 57 | } 58 | 59 | fn __deepcopy__(&self, py: Python, _memo: &Bound) -> PyResult> { 60 | Py::new(py, self.clone()) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/pendulum/_pendulum.pyi: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from datetime import date 4 | from datetime import datetime 5 | from datetime import time 6 | from typing import NamedTuple 7 | 8 | class Duration: 9 | years: int = 0 10 | months: int = 0 11 | weeks: int = 0 12 | days: int = 0 13 | remaining_days: int = 0 14 | hours: int = 0 15 | minutes: int = 0 16 | seconds: int = 0 17 | remaining_seconds: int = 0 18 | microseconds: int = 0 19 | 20 | class PreciseDiff(NamedTuple): 21 | years: int 22 | months: int 23 | days: int 24 | hours: int 25 | minutes: int 26 | seconds: int 27 | microseconds: int 28 | total_days: int 29 | 30 | def parse_iso8601( 31 | text: str, 32 | ) -> datetime | date | time | Duration: ... 33 | def days_in_year(year: int) -> int: ... 34 | def is_leap(year: int) -> bool: ... 35 | def is_long_year(year: int) -> bool: ... 36 | def local_time( 37 | unix_time: int, utc_offset: int, microseconds: int 38 | ) -> tuple[int, int, int, int, int, int, int]: ... 39 | def precise_diff(d1: datetime | date, d2: datetime | date) -> PreciseDiff: ... 40 | def week_day(year: int, month: int, day: int) -> int: ... 41 | -------------------------------------------------------------------------------- /src/pendulum/constants.py: -------------------------------------------------------------------------------- 1 | # The day constants 2 | from __future__ import annotations 3 | 4 | 5 | # Number of X in Y. 6 | YEARS_PER_CENTURY = 100 7 | YEARS_PER_DECADE = 10 8 | MONTHS_PER_YEAR = 12 9 | WEEKS_PER_YEAR = 52 10 | DAYS_PER_WEEK = 7 11 | HOURS_PER_DAY = 24 12 | MINUTES_PER_HOUR = 60 13 | SECONDS_PER_MINUTE = 60 14 | SECONDS_PER_HOUR = MINUTES_PER_HOUR * SECONDS_PER_MINUTE 15 | SECONDS_PER_DAY = HOURS_PER_DAY * SECONDS_PER_HOUR 16 | US_PER_SECOND = 1000000 17 | 18 | # Formats 19 | ATOM = "YYYY-MM-DDTHH:mm:ssZ" 20 | COOKIE = "dddd, DD-MMM-YYYY HH:mm:ss zz" 21 | ISO8601 = "YYYY-MM-DDTHH:mm:ssZ" 22 | ISO8601_EXTENDED = "YYYY-MM-DDTHH:mm:ss.SSSSSSZ" 23 | RFC822 = "ddd, DD MMM YY HH:mm:ss ZZ" 24 | RFC850 = "dddd, DD-MMM-YY HH:mm:ss zz" 25 | RFC1036 = "ddd, DD MMM YY HH:mm:ss ZZ" 26 | RFC1123 = "ddd, DD MMM YYYY HH:mm:ss ZZ" 27 | RFC2822 = "ddd, DD MMM YYYY HH:mm:ss ZZ" 28 | RFC3339 = ISO8601 29 | RFC3339_EXTENDED = ISO8601_EXTENDED 30 | RSS = "ddd, DD MMM YYYY HH:mm:ss ZZ" 31 | W3C = ISO8601 32 | 33 | 34 | EPOCH_YEAR = 1970 35 | 36 | DAYS_PER_N_YEAR = 365 37 | DAYS_PER_L_YEAR = 366 38 | 39 | USECS_PER_SEC = 1000000 40 | 41 | SECS_PER_MIN = 60 42 | SECS_PER_HOUR = 60 * SECS_PER_MIN 43 | SECS_PER_DAY = SECS_PER_HOUR * 24 44 | 45 | # 400-year chunks always have 146097 days (20871 weeks). 46 | SECS_PER_400_YEARS = 146097 * SECS_PER_DAY 47 | 48 | # The number of seconds in an aligned 100-year chunk, for those that 49 | # do not begin with a leap year and those that do respectively. 50 | SECS_PER_100_YEARS = ( 51 | (76 * DAYS_PER_N_YEAR + 24 * DAYS_PER_L_YEAR) * SECS_PER_DAY, 52 | (75 * DAYS_PER_N_YEAR + 25 * DAYS_PER_L_YEAR) * SECS_PER_DAY, 53 | ) 54 | 55 | # The number of seconds in an aligned 4-year chunk, for those that 56 | # do not begin with a leap year and those that do respectively. 57 | SECS_PER_4_YEARS = ( 58 | (4 * DAYS_PER_N_YEAR + 0 * DAYS_PER_L_YEAR) * SECS_PER_DAY, 59 | (3 * DAYS_PER_N_YEAR + 1 * DAYS_PER_L_YEAR) * SECS_PER_DAY, 60 | ) 61 | 62 | # The number of seconds in non-leap and leap years respectively. 63 | SECS_PER_YEAR = (DAYS_PER_N_YEAR * SECS_PER_DAY, DAYS_PER_L_YEAR * SECS_PER_DAY) 64 | 65 | DAYS_PER_YEAR = (DAYS_PER_N_YEAR, DAYS_PER_L_YEAR) 66 | 67 | # The month lengths in non-leap and leap years respectively. 68 | DAYS_PER_MONTHS = ( 69 | (-1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31), 70 | (-1, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31), 71 | ) 72 | 73 | # The day offsets of the beginning of each (1-based) month in non-leap 74 | # and leap years respectively. 75 | # For example, in a leap year there are 335 days before December. 76 | MONTHS_OFFSETS = ( 77 | (-1, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365), 78 | (-1, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366), 79 | ) 80 | 81 | DAY_OF_WEEK_TABLE = (0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4) 82 | 83 | TM_SUNDAY = 0 84 | TM_MONDAY = 1 85 | TM_TUESDAY = 2 86 | TM_WEDNESDAY = 3 87 | TM_THURSDAY = 4 88 | TM_FRIDAY = 5 89 | TM_SATURDAY = 6 90 | 91 | TM_JANUARY = 0 92 | TM_FEBRUARY = 1 93 | TM_MARCH = 2 94 | TM_APRIL = 3 95 | TM_MAY = 4 96 | TM_JUNE = 5 97 | TM_JULY = 6 98 | TM_AUGUST = 7 99 | TM_SEPTEMBER = 8 100 | TM_OCTOBER = 9 101 | TM_NOVEMBER = 10 102 | TM_DECEMBER = 11 103 | -------------------------------------------------------------------------------- /src/pendulum/day.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from enum import IntEnum 4 | 5 | 6 | class WeekDay(IntEnum): 7 | MONDAY = 0 8 | TUESDAY = 1 9 | WEDNESDAY = 2 10 | THURSDAY = 3 11 | FRIDAY = 4 12 | SATURDAY = 5 13 | SUNDAY = 6 14 | -------------------------------------------------------------------------------- /src/pendulum/exceptions.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from pendulum.parsing.exceptions import ParserError 4 | 5 | 6 | class PendulumException(Exception): 7 | pass 8 | 9 | 10 | __all__ = [ 11 | "ParserError", 12 | "PendulumException", 13 | ] 14 | -------------------------------------------------------------------------------- /src/pendulum/formatting/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from pendulum.formatting.formatter import Formatter 4 | 5 | 6 | __all__ = ["Formatter"] 7 | -------------------------------------------------------------------------------- /src/pendulum/locales/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-pendulum/pendulum/fc386be2623f711364c599df3e208eceb4dfa23b/src/pendulum/locales/__init__.py -------------------------------------------------------------------------------- /src/pendulum/locales/bg/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-pendulum/pendulum/fc386be2623f711364c599df3e208eceb4dfa23b/src/pendulum/locales/bg/__init__.py -------------------------------------------------------------------------------- /src/pendulum/locales/bg/custom.py: -------------------------------------------------------------------------------- 1 | """ 2 | bg custom locale file. 3 | """ 4 | from __future__ import annotations 5 | 6 | 7 | translations = { 8 | # Relative time 9 | "ago": "преди {}", 10 | "from_now": "след {}", 11 | "after": "след {0}", 12 | "before": "преди {0}", 13 | # Date formats 14 | "date_formats": { 15 | "LTS": "HH:mm:ss", 16 | "LT": "HH:mm", 17 | "L": "DD.MM.YYYY", 18 | "LL": "D MMMM YYYY г.", 19 | "LLL": "D MMMM YYYY г., HH:mm", 20 | "LLLL": "dddd, D MMMM YYYY г., HH:mm", 21 | }, 22 | } 23 | -------------------------------------------------------------------------------- /src/pendulum/locales/cs/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-pendulum/pendulum/fc386be2623f711364c599df3e208eceb4dfa23b/src/pendulum/locales/cs/__init__.py -------------------------------------------------------------------------------- /src/pendulum/locales/cs/custom.py: -------------------------------------------------------------------------------- 1 | """ 2 | cs custom locale file. 3 | """ 4 | from __future__ import annotations 5 | 6 | 7 | translations = { 8 | "units": {"few_second": "pár vteřin"}, 9 | # Relative time 10 | "ago": "{} zpět", 11 | "from_now": "za {}", 12 | "after": "{0} po", 13 | "before": "{0} zpět", 14 | # Ordinals 15 | "ordinal": {"one": ".", "two": ".", "few": ".", "other": "."}, 16 | # Date formats 17 | "date_formats": { 18 | "LTS": "h:mm:ss", 19 | "LT": "h:mm", 20 | "L": "DD. M. YYYY", 21 | "LL": "D. MMMM, YYYY", 22 | "LLL": "D. MMMM, YYYY h:mm", 23 | "LLLL": "dddd, D. MMMM, YYYY h:mm", 24 | }, 25 | } 26 | -------------------------------------------------------------------------------- /src/pendulum/locales/da/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-pendulum/pendulum/fc386be2623f711364c599df3e208eceb4dfa23b/src/pendulum/locales/da/__init__.py -------------------------------------------------------------------------------- /src/pendulum/locales/da/custom.py: -------------------------------------------------------------------------------- 1 | """ 2 | da custom locale file. 3 | """ 4 | from __future__ import annotations 5 | 6 | 7 | translations = { 8 | # Relative time 9 | "after": "{0} efter", 10 | "before": "{0} før", 11 | # Date formats 12 | "date_formats": { 13 | "LTS": "HH:mm:ss", 14 | "LT": "HH:mm", 15 | "LLLL": "dddd [d.] D. MMMM YYYY HH:mm", 16 | "LLL": "D. MMMM YYYY HH:mm", 17 | "LL": "D. MMMM YYYY", 18 | "L": "DD/MM/YYYY", 19 | }, 20 | } 21 | -------------------------------------------------------------------------------- /src/pendulum/locales/de/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-pendulum/pendulum/fc386be2623f711364c599df3e208eceb4dfa23b/src/pendulum/locales/de/__init__.py -------------------------------------------------------------------------------- /src/pendulum/locales/de/custom.py: -------------------------------------------------------------------------------- 1 | """ 2 | de custom locale file. 3 | """ 4 | from __future__ import annotations 5 | 6 | 7 | translations = { 8 | # Relative time 9 | "after": "{0} später", 10 | "before": "{0} zuvor", 11 | "units_relative": { 12 | "year": { 13 | "future": {"one": "{0} Jahr", "other": "{0} Jahren"}, 14 | "past": {"one": "{0} Jahr", "other": "{0} Jahren"}, 15 | }, 16 | "month": { 17 | "future": {"one": "{0} Monat", "other": "{0} Monaten"}, 18 | "past": {"one": "{0} Monat", "other": "{0} Monaten"}, 19 | }, 20 | "week": { 21 | "future": {"one": "{0} Woche", "other": "{0} Wochen"}, 22 | "past": {"one": "{0} Woche", "other": "{0} Wochen"}, 23 | }, 24 | "day": { 25 | "future": {"one": "{0} Tag", "other": "{0} Tagen"}, 26 | "past": {"one": "{0} Tag", "other": "{0} Tagen"}, 27 | }, 28 | }, 29 | # Date formats 30 | "date_formats": { 31 | "LTS": "HH:mm:ss", 32 | "LT": "HH:mm", 33 | "LLLL": "dddd, D. MMMM YYYY HH:mm", 34 | "LLL": "D. MMMM YYYY HH:mm", 35 | "LL": "D. MMMM YYYY", 36 | "L": "DD.MM.YYYY", 37 | }, 38 | } 39 | -------------------------------------------------------------------------------- /src/pendulum/locales/en/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-pendulum/pendulum/fc386be2623f711364c599df3e208eceb4dfa23b/src/pendulum/locales/en/__init__.py -------------------------------------------------------------------------------- /src/pendulum/locales/en/custom.py: -------------------------------------------------------------------------------- 1 | """ 2 | en custom locale file. 3 | """ 4 | from __future__ import annotations 5 | 6 | 7 | translations = { 8 | "units": {"few_second": "a few seconds"}, 9 | # Relative time 10 | "ago": "{} ago", 11 | "from_now": "in {}", 12 | "after": "{0} after", 13 | "before": "{0} before", 14 | # Ordinals 15 | "ordinal": {"one": "st", "two": "nd", "few": "rd", "other": "th"}, 16 | # Date formats 17 | "date_formats": { 18 | "LTS": "h:mm:ss A", 19 | "LT": "h:mm A", 20 | "L": "MM/DD/YYYY", 21 | "LL": "MMMM D, YYYY", 22 | "LLL": "MMMM D, YYYY h:mm A", 23 | "LLLL": "dddd, MMMM D, YYYY h:mm A", 24 | }, 25 | } 26 | -------------------------------------------------------------------------------- /src/pendulum/locales/en_gb/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-pendulum/pendulum/fc386be2623f711364c599df3e208eceb4dfa23b/src/pendulum/locales/en_gb/__init__.py -------------------------------------------------------------------------------- /src/pendulum/locales/en_gb/custom.py: -------------------------------------------------------------------------------- 1 | """ 2 | en-gb custom locale file. 3 | """ 4 | from __future__ import annotations 5 | 6 | 7 | translations = { 8 | "units": {"few_second": "a few seconds"}, 9 | # Relative time 10 | "ago": "{} ago", 11 | "from_now": "in {}", 12 | "after": "{0} after", 13 | "before": "{0} before", 14 | # Ordinals 15 | "ordinal": {"one": "st", "two": "nd", "few": "rd", "other": "th"}, 16 | # Date formats 17 | "date_formats": { 18 | "LTS": "HH:mm:ss", 19 | "LT": "HH:mm", 20 | "L": "DD/MM/YYYY", 21 | "LL": "D MMMM YYYY", 22 | "LLL": "D MMMM YYYY HH:mm", 23 | "LLLL": "dddd, D MMMM YYYY HH:mm", 24 | }, 25 | } 26 | -------------------------------------------------------------------------------- /src/pendulum/locales/en_us/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-pendulum/pendulum/fc386be2623f711364c599df3e208eceb4dfa23b/src/pendulum/locales/en_us/__init__.py -------------------------------------------------------------------------------- /src/pendulum/locales/en_us/custom.py: -------------------------------------------------------------------------------- 1 | """ 2 | en-us custom locale file. 3 | """ 4 | from __future__ import annotations 5 | 6 | 7 | translations = { 8 | "units": {"few_second": "a few seconds"}, 9 | # Relative time 10 | "ago": "{} ago", 11 | "from_now": "in {}", 12 | "after": "{0} after", 13 | "before": "{0} before", 14 | # Ordinals 15 | "ordinal": {"one": "st", "two": "nd", "few": "rd", "other": "th"}, 16 | # Date formats 17 | "date_formats": { 18 | "LTS": "h:mm:ss A", 19 | "LT": "h:mm A", 20 | "L": "MM/DD/YYYY", 21 | "LL": "MMMM D, YYYY", 22 | "LLL": "MMMM D, YYYY h:mm A", 23 | "LLLL": "dddd, MMMM D, YYYY h:mm A", 24 | }, 25 | } 26 | -------------------------------------------------------------------------------- /src/pendulum/locales/es/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-pendulum/pendulum/fc386be2623f711364c599df3e208eceb4dfa23b/src/pendulum/locales/es/__init__.py -------------------------------------------------------------------------------- /src/pendulum/locales/es/custom.py: -------------------------------------------------------------------------------- 1 | """ 2 | es custom locale file. 3 | """ 4 | from __future__ import annotations 5 | 6 | 7 | translations = { 8 | "units": {"few_second": "unos segundos"}, 9 | # Relative time 10 | "ago": "hace {0}", 11 | "from_now": "dentro de {0}", 12 | "after": "{0} después", 13 | "before": "{0} antes", 14 | # Ordinals 15 | "ordinal": {"other": "º"}, 16 | # Date formats 17 | "date_formats": { 18 | "LTS": "H:mm:ss", 19 | "LT": "H:mm", 20 | "LLLL": "dddd, D [de] MMMM [de] YYYY H:mm", 21 | "LLL": "D [de] MMMM [de] YYYY H:mm", 22 | "LL": "D [de] MMMM [de] YYYY", 23 | "L": "DD/MM/YYYY", 24 | }, 25 | } 26 | -------------------------------------------------------------------------------- /src/pendulum/locales/fa/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-pendulum/pendulum/fc386be2623f711364c599df3e208eceb4dfa23b/src/pendulum/locales/fa/__init__.py -------------------------------------------------------------------------------- /src/pendulum/locales/fa/custom.py: -------------------------------------------------------------------------------- 1 | """ 2 | fa custom locale file. 3 | """ 4 | from __future__ import annotations 5 | 6 | 7 | translations = { 8 | # Relative time 9 | "after": "{0} پس از", 10 | "before": "{0} پیش از", 11 | # Date formats 12 | "date_formats": { 13 | "LTS": "HH:mm:ss", 14 | "LT": "HH:mm", 15 | "LLLL": "dddd, D MMMM YYYY HH:mm", 16 | "LLL": "D MMMM YYYY HH:mm", 17 | "LL": "D MMMM YYYY", 18 | "L": "DD/MM/YYYY", 19 | }, 20 | } 21 | -------------------------------------------------------------------------------- /src/pendulum/locales/fo/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-pendulum/pendulum/fc386be2623f711364c599df3e208eceb4dfa23b/src/pendulum/locales/fo/__init__.py -------------------------------------------------------------------------------- /src/pendulum/locales/fo/custom.py: -------------------------------------------------------------------------------- 1 | """ 2 | fo custom locale file. 3 | """ 4 | from __future__ import annotations 5 | 6 | 7 | translations = { 8 | # Relative time 9 | "after": "{0} aftaná", 10 | "before": "{0} áðrenn", 11 | # Ordinals 12 | "ordinal": {"other": "."}, 13 | # Date formats 14 | "date_formats": { 15 | "LTS": "HH:mm:ss", 16 | "LT": "HH:mm", 17 | "LLLL": "dddd D. MMMM, YYYY HH:mm", 18 | "LLL": "D MMMM YYYY HH:mm", 19 | "LL": "D MMMM YYYY", 20 | "L": "DD/MM/YYYY", 21 | }, 22 | } 23 | -------------------------------------------------------------------------------- /src/pendulum/locales/fr/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-pendulum/pendulum/fc386be2623f711364c599df3e208eceb4dfa23b/src/pendulum/locales/fr/__init__.py -------------------------------------------------------------------------------- /src/pendulum/locales/fr/custom.py: -------------------------------------------------------------------------------- 1 | """ 2 | fr custom locale file. 3 | """ 4 | from __future__ import annotations 5 | 6 | 7 | translations = { 8 | "units": {"few_second": "quelques secondes"}, 9 | # Relative Time 10 | "ago": "il y a {0}", 11 | "from_now": "dans {0}", 12 | "after": "{0} après", 13 | "before": "{0} avant", 14 | # Ordinals 15 | "ordinal": {"one": "er", "other": "e"}, 16 | # Date formats 17 | "date_formats": { 18 | "LTS": "HH:mm:ss", 19 | "LT": "HH:mm", 20 | "LLLL": "dddd D MMMM YYYY HH:mm", 21 | "LLL": "D MMMM YYYY HH:mm", 22 | "LL": "D MMMM YYYY", 23 | "L": "DD/MM/YYYY", 24 | }, 25 | } 26 | -------------------------------------------------------------------------------- /src/pendulum/locales/he/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-pendulum/pendulum/fc386be2623f711364c599df3e208eceb4dfa23b/src/pendulum/locales/he/__init__.py -------------------------------------------------------------------------------- /src/pendulum/locales/he/custom.py: -------------------------------------------------------------------------------- 1 | """ 2 | he custom locale file. 3 | """ 4 | from __future__ import annotations 5 | 6 | 7 | translations = { 8 | "units": {"few_second": "כמה שניות"}, 9 | # Relative time 10 | "ago": "לפני {0}", 11 | "from_now": "תוך {0}", 12 | "after": "בעוד {0}", 13 | "before": "{0} קודם", 14 | # Ordinals 15 | "ordinal": {"other": "º"}, 16 | # Date formats 17 | "date_formats": { 18 | "LTS": "H:mm:ss", 19 | "LT": "H:mm", 20 | "LLLL": "dddd, D [ב] MMMM [ב] YYYY H:mm", 21 | "LLL": "D [ב] MMMM [ב] YYYY H:mm", 22 | "LL": "D [ב] MMMM [ב] YYYY", 23 | "L": "DD/MM/YYYY", 24 | }, 25 | } 26 | -------------------------------------------------------------------------------- /src/pendulum/locales/id/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-pendulum/pendulum/fc386be2623f711364c599df3e208eceb4dfa23b/src/pendulum/locales/id/__init__.py -------------------------------------------------------------------------------- /src/pendulum/locales/id/custom.py: -------------------------------------------------------------------------------- 1 | """ 2 | id custom locale file. 3 | """ 4 | from __future__ import annotations 5 | 6 | 7 | translations = { 8 | "units": {"few_second": "beberapa detik"}, 9 | "ago": "{} yang lalu", 10 | "from_now": "dalam {}", 11 | "after": "{0} kemudian", 12 | "before": "{0} yang lalu", 13 | "date_formats": { 14 | "LTS": "HH:mm:ss", 15 | "LT": "HH:mm", 16 | "LLLL": "dddd [d.] D. MMMM YYYY HH:mm", 17 | "LLL": "D. MMMM YYYY HH:mm", 18 | "LL": "D. MMMM YYYY", 19 | "L": "DD/MM/YYYY", 20 | }, 21 | } 22 | -------------------------------------------------------------------------------- /src/pendulum/locales/it/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-pendulum/pendulum/fc386be2623f711364c599df3e208eceb4dfa23b/src/pendulum/locales/it/__init__.py -------------------------------------------------------------------------------- /src/pendulum/locales/it/custom.py: -------------------------------------------------------------------------------- 1 | """ 2 | it custom locale file. 3 | """ 4 | from __future__ import annotations 5 | 6 | 7 | translations = { 8 | "units": {"few_second": "alcuni secondi"}, 9 | # Relative Time 10 | "ago": "{0} fa", 11 | "from_now": "in {0}", 12 | "after": "{0} dopo", 13 | "before": "{0} prima", 14 | # Ordinals 15 | "ordinal": {"other": "°"}, 16 | # Date formats 17 | "date_formats": { 18 | "LTS": "H:mm:ss", 19 | "LT": "H:mm", 20 | "L": "DD/MM/YYYY", 21 | "LL": "D MMMM YYYY", 22 | "LLL": "D MMMM YYYY [alle] H:mm", 23 | "LLLL": "dddd, D MMMM YYYY [alle] H:mm", 24 | }, 25 | } 26 | -------------------------------------------------------------------------------- /src/pendulum/locales/ja/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-pendulum/pendulum/fc386be2623f711364c599df3e208eceb4dfa23b/src/pendulum/locales/ja/__init__.py -------------------------------------------------------------------------------- /src/pendulum/locales/ja/custom.py: -------------------------------------------------------------------------------- 1 | """ 2 | ja custom locale file. 3 | """ 4 | from __future__ import annotations 5 | 6 | 7 | translations = { 8 | "units": {"few_second": "数秒"}, 9 | # Relative time 10 | "ago": "{} 前に", 11 | "from_now": "今から {}", 12 | "after": "{0} 後", 13 | "before": "{0} 前", 14 | # Date formats 15 | "date_formats": { 16 | "LTS": "h:mm:ss A", 17 | "LT": "h:mm A", 18 | "L": "MM/DD/YYYY", 19 | "LL": "MMMM D, YYYY", 20 | "LLL": "MMMM D, YYYY h:mm A", 21 | "LLLL": "dddd, MMMM D, YYYY h:mm A", 22 | }, 23 | } 24 | -------------------------------------------------------------------------------- /src/pendulum/locales/ko/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-pendulum/pendulum/fc386be2623f711364c599df3e208eceb4dfa23b/src/pendulum/locales/ko/__init__.py -------------------------------------------------------------------------------- /src/pendulum/locales/ko/custom.py: -------------------------------------------------------------------------------- 1 | """ 2 | ko custom locale file. 3 | """ 4 | from __future__ import annotations 5 | 6 | 7 | translations = { 8 | # Relative time 9 | "after": "{0} 후", 10 | "before": "{0} 전", 11 | # Date formats 12 | "date_formats": { 13 | "LTS": "A h시 m분 s초", 14 | "LT": "A h시 m분", 15 | "LLLL": "YYYY년 MMMM D일 dddd A h시 m분", 16 | "LLL": "YYYY년 MMMM D일 A h시 m분", 17 | "LL": "YYYY년 MMMM D일", 18 | "L": "YYYY.MM.DD", 19 | }, 20 | } 21 | -------------------------------------------------------------------------------- /src/pendulum/locales/ko/locale.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from pendulum.locales.ko.custom import translations as custom_translations 4 | 5 | 6 | """ 7 | ko locale file. 8 | 9 | It has been generated automatically and must not be modified directly. 10 | """ 11 | 12 | 13 | locale = { 14 | "plural": lambda n: "other", 15 | "ordinal": lambda n: "other", 16 | "translations": { 17 | "days": { 18 | "abbreviated": {0: "월", 1: "화", 2: "수", 3: "목", 4: "금", 5: "토", 6: "일"}, 19 | "narrow": {0: "월", 1: "화", 2: "수", 3: "목", 4: "금", 5: "토", 6: "일"}, 20 | "short": {0: "월", 1: "화", 2: "수", 3: "목", 4: "금", 5: "토", 6: "일"}, 21 | "wide": { 22 | 0: "월요일", 23 | 1: "화요일", 24 | 2: "수요일", 25 | 3: "목요일", 26 | 4: "금요일", 27 | 5: "토요일", 28 | 6: "일요일", 29 | }, 30 | }, 31 | "months": { 32 | "abbreviated": { 33 | 1: "1월", 34 | 2: "2월", 35 | 3: "3월", 36 | 4: "4월", 37 | 5: "5월", 38 | 6: "6월", 39 | 7: "7월", 40 | 8: "8월", 41 | 9: "9월", 42 | 10: "10월", 43 | 11: "11월", 44 | 12: "12월", 45 | }, 46 | "narrow": { 47 | 1: "1월", 48 | 2: "2월", 49 | 3: "3월", 50 | 4: "4월", 51 | 5: "5월", 52 | 6: "6월", 53 | 7: "7월", 54 | 8: "8월", 55 | 9: "9월", 56 | 10: "10월", 57 | 11: "11월", 58 | 12: "12월", 59 | }, 60 | "wide": { 61 | 1: "1월", 62 | 2: "2월", 63 | 3: "3월", 64 | 4: "4월", 65 | 5: "5월", 66 | 6: "6월", 67 | 7: "7월", 68 | 8: "8월", 69 | 9: "9월", 70 | 10: "10월", 71 | 11: "11월", 72 | 12: "12월", 73 | }, 74 | }, 75 | "units": { 76 | "year": {"other": "{0}년"}, 77 | "month": {"other": "{0}개월"}, 78 | "week": {"other": "{0}주"}, 79 | "day": {"other": "{0}일"}, 80 | "hour": {"other": "{0}시간"}, 81 | "minute": {"other": "{0}분"}, 82 | "second": {"other": "{0}초"}, 83 | "microsecond": {"other": "{0}마이크로초"}, 84 | }, 85 | "relative": { 86 | "year": {"future": {"other": "{0}년 후"}, "past": {"other": "{0}년 전"}}, 87 | "month": {"future": {"other": "{0}개월 후"}, "past": {"other": "{0}개월 전"}}, 88 | "week": {"future": {"other": "{0}주 후"}, "past": {"other": "{0}주 전"}}, 89 | "day": {"future": {"other": "{0}일 후"}, "past": {"other": "{0}일 전"}}, 90 | "hour": {"future": {"other": "{0}시간 후"}, "past": {"other": "{0}시간 전"}}, 91 | "minute": {"future": {"other": "{0}분 후"}, "past": {"other": "{0}분 전"}}, 92 | "second": {"future": {"other": "{0}초 후"}, "past": {"other": "{0}초 전"}}, 93 | }, 94 | "day_periods": { 95 | "midnight": "자정", 96 | "am": "오전", 97 | "noon": "정오", 98 | "pm": "오후", 99 | "morning1": "새벽", 100 | "morning2": "오전", 101 | "afternoon1": "오후", 102 | "evening1": "저녁", 103 | "night1": "밤", 104 | }, 105 | "week_data": { 106 | "min_days": 1, 107 | "first_day": 0, 108 | "weekend_start": 5, 109 | "weekend_end": 6, 110 | }, 111 | }, 112 | "custom": custom_translations, 113 | } 114 | -------------------------------------------------------------------------------- /src/pendulum/locales/locale.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import re 4 | 5 | from importlib import import_module, resources 6 | from pathlib import Path 7 | from typing import Any 8 | from typing import ClassVar 9 | from typing import Dict 10 | from typing import cast 11 | 12 | 13 | class Locale: 14 | """ 15 | Represent a specific locale. 16 | """ 17 | 18 | _cache: ClassVar[dict[str, Locale]] = {} 19 | 20 | def __init__(self, locale: str, data: Any) -> None: 21 | self._locale: str = locale 22 | self._data: Any = data 23 | self._key_cache: dict[str, str] = {} 24 | 25 | @classmethod 26 | def load(cls, locale: str | Locale) -> Locale: 27 | if isinstance(locale, Locale): 28 | return locale 29 | 30 | locale = cls.normalize_locale(locale) 31 | if locale in cls._cache: 32 | return cls._cache[locale] 33 | 34 | # Checking locale existence 35 | actual_locale = locale 36 | locale_path = cast(Path, resources.files(__package__).joinpath(actual_locale)) 37 | while not locale_path.exists(): 38 | if actual_locale == locale: 39 | raise ValueError(f"Locale [{locale}] does not exist.") 40 | 41 | actual_locale = actual_locale.split("_")[0] 42 | 43 | m = import_module(f"pendulum.locales.{actual_locale}.locale") 44 | 45 | cls._cache[locale] = cls(locale, m.locale) 46 | 47 | return cls._cache[locale] 48 | 49 | @classmethod 50 | def normalize_locale(cls, locale: str) -> str: 51 | m = re.fullmatch("([a-z]{2})[-_]([a-z]{2})", locale, re.I) 52 | if m: 53 | return f"{m.group(1).lower()}_{m.group(2).lower()}" 54 | else: 55 | return locale.lower() 56 | 57 | def get(self, key: str, default: Any | None = None) -> Any: 58 | if key in self._key_cache: 59 | return self._key_cache[key] 60 | 61 | parts = key.split(".") 62 | try: 63 | result = self._data[parts[0]] 64 | for part in parts[1:]: 65 | result = result[part] 66 | except KeyError: 67 | result = default 68 | 69 | self._key_cache[key] = result 70 | 71 | return self._key_cache[key] 72 | 73 | def translation(self, key: str) -> Any: 74 | return self.get(f"translations.{key}") 75 | 76 | def plural(self, number: int) -> str: 77 | return cast(str, self._data["plural"](number)) 78 | 79 | def ordinal(self, number: int) -> str: 80 | return cast(str, self._data["ordinal"](number)) 81 | 82 | def ordinalize(self, number: int) -> str: 83 | ordinal = self.get(f"custom.ordinal.{self.ordinal(number)}") 84 | 85 | if not ordinal: 86 | return f"{number}" 87 | 88 | return f"{number}{ordinal}" 89 | 90 | def match_translation(self, key: str, value: Any) -> dict[str, str] | None: 91 | translations = self.translation(key) 92 | if value not in translations.values(): 93 | return None 94 | 95 | return cast(Dict[str, str], {v: k for k, v in translations.items()}[value]) 96 | 97 | def __repr__(self) -> str: 98 | return f"{self.__class__.__name__}('{self._locale}')" 99 | -------------------------------------------------------------------------------- /src/pendulum/locales/lt/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-pendulum/pendulum/fc386be2623f711364c599df3e208eceb4dfa23b/src/pendulum/locales/lt/__init__.py -------------------------------------------------------------------------------- /src/pendulum/locales/nb/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-pendulum/pendulum/fc386be2623f711364c599df3e208eceb4dfa23b/src/pendulum/locales/nb/__init__.py -------------------------------------------------------------------------------- /src/pendulum/locales/nb/custom.py: -------------------------------------------------------------------------------- 1 | """ 2 | nn custom locale file. 3 | """ 4 | from __future__ import annotations 5 | 6 | 7 | translations = { 8 | # Relative time 9 | "after": "{0} etter", 10 | "before": "{0} før", 11 | # Ordinals 12 | "ordinal": {"one": ".", "two": ".", "few": ".", "other": "."}, 13 | # Date formats 14 | "date_formats": { 15 | "LTS": "HH:mm:ss", 16 | "LT": "HH:mm", 17 | "LLLL": "dddd Do MMMM YYYY HH:mm", 18 | "LLL": "Do MMMM YYYY HH:mm", 19 | "LL": "Do MMMM YYYY", 20 | "L": "DD.MM.YYYY", 21 | }, 22 | } 23 | -------------------------------------------------------------------------------- /src/pendulum/locales/nl/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-pendulum/pendulum/fc386be2623f711364c599df3e208eceb4dfa23b/src/pendulum/locales/nl/__init__.py -------------------------------------------------------------------------------- /src/pendulum/locales/nl/custom.py: -------------------------------------------------------------------------------- 1 | """ 2 | nl custom locale file. 3 | """ 4 | from __future__ import annotations 5 | 6 | 7 | translations = { 8 | "units": {"few_second": "enkele seconden"}, 9 | # Relative time 10 | "ago": "{} geleden", 11 | "from_now": "over {}", 12 | "after": "{0} later", 13 | "before": "{0} eerder", 14 | # Ordinals 15 | "ordinal": {"other": "e"}, 16 | # Date formats 17 | "date_formats": { 18 | "L": "DD-MM-YYYY", 19 | "LL": "D MMMM YYYY", 20 | "LLL": "D MMMM YYYY HH:mm", 21 | "LLLL": "dddd D MMMM YYYY HH:mm", 22 | "LT": "HH:mm", 23 | "LTS": "HH:mm:ss", 24 | }, 25 | } 26 | -------------------------------------------------------------------------------- /src/pendulum/locales/nn/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-pendulum/pendulum/fc386be2623f711364c599df3e208eceb4dfa23b/src/pendulum/locales/nn/__init__.py -------------------------------------------------------------------------------- /src/pendulum/locales/nn/custom.py: -------------------------------------------------------------------------------- 1 | """ 2 | nn custom locale file. 3 | """ 4 | from __future__ import annotations 5 | 6 | 7 | translations = { 8 | # Relative time 9 | "after": "{0} etter", 10 | "before": "{0} før", 11 | # Ordinals 12 | "ordinal": {"one": ".", "two": ".", "few": ".", "other": "."}, 13 | # Date formats 14 | "date_formats": { 15 | "LTS": "HH:mm:ss", 16 | "LT": "HH:mm", 17 | "LLLL": "dddd Do MMMM YYYY HH:mm", 18 | "LLL": "Do MMMM YYYY HH:mm", 19 | "LL": "Do MMMM YYYY", 20 | "L": "DD.MM.YYYY", 21 | }, 22 | } 23 | -------------------------------------------------------------------------------- /src/pendulum/locales/pl/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-pendulum/pendulum/fc386be2623f711364c599df3e208eceb4dfa23b/src/pendulum/locales/pl/__init__.py -------------------------------------------------------------------------------- /src/pendulum/locales/pl/custom.py: -------------------------------------------------------------------------------- 1 | """ 2 | pl custom locale file. 3 | """ 4 | from __future__ import annotations 5 | 6 | 7 | translations = { 8 | "units": {"few_second": "kilka sekund"}, 9 | # Relative time 10 | "ago": "{} temu", 11 | "from_now": "za {}", 12 | "after": "{0} po", 13 | "before": "{0} przed", 14 | # Date formats 15 | "date_formats": { 16 | "LTS": "HH:mm:ss", 17 | "LT": "HH:mm", 18 | "L": "DD.MM.YYYY", 19 | "LL": "D MMMM YYYY", 20 | "LLL": "D MMMM YYYY HH:mm", 21 | "LLLL": "dddd, D MMMM YYYY HH:mm", 22 | }, 23 | } 24 | -------------------------------------------------------------------------------- /src/pendulum/locales/pt_br/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-pendulum/pendulum/fc386be2623f711364c599df3e208eceb4dfa23b/src/pendulum/locales/pt_br/__init__.py -------------------------------------------------------------------------------- /src/pendulum/locales/pt_br/custom.py: -------------------------------------------------------------------------------- 1 | """ 2 | pt-br custom locale file. 3 | """ 4 | from __future__ import annotations 5 | 6 | 7 | translations = { 8 | # Relative time 9 | "after": "após {0}", 10 | "before": "{0} atrás", 11 | # Date formats 12 | "date_formats": { 13 | "LTS": "HH:mm:ss", 14 | "LT": "HH:mm", 15 | "LLLL": "dddd, D [de] MMMM [de] YYYY [às] HH:mm", 16 | "LLL": "D [de] MMMM [de] YYYY [às] HH:mm", 17 | "LL": "D [de] MMMM [de] YYYY", 18 | "L": "DD/MM/YYYY", 19 | }, 20 | } 21 | -------------------------------------------------------------------------------- /src/pendulum/locales/ru/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-pendulum/pendulum/fc386be2623f711364c599df3e208eceb4dfa23b/src/pendulum/locales/ru/__init__.py -------------------------------------------------------------------------------- /src/pendulum/locales/ru/custom.py: -------------------------------------------------------------------------------- 1 | """ 2 | ru custom locale file. 3 | """ 4 | from __future__ import annotations 5 | 6 | 7 | translations = { 8 | # Relative time 9 | "ago": "{} назад", 10 | "from_now": "через {}", 11 | "after": "{0} после", 12 | "before": "{0} до", 13 | # Date formats 14 | "date_formats": { 15 | "LTS": "HH:mm:ss", 16 | "LT": "HH:mm", 17 | "L": "DD.MM.YYYY", 18 | "LL": "D MMMM YYYY г.", 19 | "LLL": "D MMMM YYYY г., HH:mm", 20 | "LLLL": "dddd, D MMMM YYYY г., HH:mm", 21 | }, 22 | } 23 | -------------------------------------------------------------------------------- /src/pendulum/locales/sk/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-pendulum/pendulum/fc386be2623f711364c599df3e208eceb4dfa23b/src/pendulum/locales/sk/__init__.py -------------------------------------------------------------------------------- /src/pendulum/locales/sk/custom.py: -------------------------------------------------------------------------------- 1 | """ 2 | sk custom locale file. 3 | """ 4 | from __future__ import annotations 5 | 6 | 7 | translations = { 8 | # Relative time 9 | "ago": "pred {}", 10 | "from_now": "o {}", 11 | "after": "{0} po", 12 | "before": "{0} pred", 13 | # Date formats 14 | "date_formats": { 15 | "LTS": "HH:mm:ss", 16 | "LT": "HH:mm", 17 | "LLLL": "dddd, D. MMMM YYYY HH:mm", 18 | "LLL": "D. MMMM YYYY HH:mm", 19 | "LL": "D. MMMM YYYY", 20 | "L": "DD.MM.YYYY", 21 | }, 22 | } 23 | -------------------------------------------------------------------------------- /src/pendulum/locales/sv/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-pendulum/pendulum/fc386be2623f711364c599df3e208eceb4dfa23b/src/pendulum/locales/sv/__init__.py -------------------------------------------------------------------------------- /src/pendulum/locales/sv/custom.py: -------------------------------------------------------------------------------- 1 | """ 2 | sv custom locale file. 3 | """ 4 | from __future__ import annotations 5 | 6 | 7 | translations = { 8 | # Relative time 9 | "ago": "{} sedan", 10 | "from_now": "från nu {}", 11 | "after": "{0} efter", 12 | "before": "{0} innan", 13 | # Date formats 14 | "date_formats": { 15 | "LTS": "HH:mm:ss", 16 | "LT": "HH:mm", 17 | "L": "YYYY-MM-DD", 18 | "LL": "D MMMM YYYY", 19 | "LLL": "D MMMM YYYY, HH:mm", 20 | "LLLL": "dddd, D MMMM YYYY, HH:mm", 21 | }, 22 | } 23 | -------------------------------------------------------------------------------- /src/pendulum/locales/tr/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-pendulum/pendulum/fc386be2623f711364c599df3e208eceb4dfa23b/src/pendulum/locales/tr/__init__.py -------------------------------------------------------------------------------- /src/pendulum/locales/tr/custom.py: -------------------------------------------------------------------------------- 1 | """ 2 | tr custom locale file. 3 | """ 4 | from __future__ import annotations 5 | 6 | 7 | translations = { 8 | # Relative time 9 | "ago": "{} önce", 10 | "from_now": "{} içinde", 11 | "after": "{0} sonra", 12 | "before": "{0} önce", 13 | # Ordinals 14 | "ordinal": {"one": ".", "two": ".", "few": ".", "other": "."}, 15 | # Date formats 16 | "date_formats": { 17 | "LTS": "h:mm:ss A", 18 | "LT": "h:mm A", 19 | "L": "MM/DD/YYYY", 20 | "LL": "MMMM D, YYYY", 21 | "LLL": "MMMM D, YYYY h:mm A", 22 | "LLLL": "dddd, MMMM D, YYYY h:mm A", 23 | }, 24 | } 25 | -------------------------------------------------------------------------------- /src/pendulum/locales/ua/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-pendulum/pendulum/fc386be2623f711364c599df3e208eceb4dfa23b/src/pendulum/locales/ua/__init__.py -------------------------------------------------------------------------------- /src/pendulum/locales/ua/custom.py: -------------------------------------------------------------------------------- 1 | """ 2 | ua custom locale file. 3 | """ 4 | from __future__ import annotations 5 | 6 | 7 | translations = { 8 | "units": {"few_second": "кілька секунд"}, 9 | # Relative time 10 | "ago": "{} тому", 11 | "from_now": "за {}", 12 | "after": "{0} посіля", 13 | "before": "{0} до", 14 | # Date formats 15 | "date_formats": { 16 | "LTS": "HH:mm:ss", 17 | "LT": "HH:mm", 18 | "L": "DD.MM.YYYY", 19 | "LL": "D MMMM YYYY р.", 20 | "LLL": "D MMMM YYYY р., HH:mm", 21 | "LLLL": "dddd, D MMMM YYYY р., HH:mm", 22 | }, 23 | } 24 | -------------------------------------------------------------------------------- /src/pendulum/locales/zh/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-pendulum/pendulum/fc386be2623f711364c599df3e208eceb4dfa23b/src/pendulum/locales/zh/__init__.py -------------------------------------------------------------------------------- /src/pendulum/locales/zh/custom.py: -------------------------------------------------------------------------------- 1 | """ 2 | zh custom locale file. 3 | """ 4 | from __future__ import annotations 5 | 6 | 7 | translations = { 8 | # Relative time 9 | "after": "{time}后", 10 | "before": "{time}前", 11 | # Date formats 12 | "date_formats": { 13 | "LTS": "Ah点m分s秒", 14 | "LT": "Ah点mm分", 15 | "LLLL": "YYYY年MMMD日ddddAh点mm分", 16 | "LLL": "YYYY年MMMD日Ah点mm分", 17 | "LL": "YYYY年MMMD日", 18 | "L": "YYYY-MM-DD", 19 | }, 20 | } 21 | -------------------------------------------------------------------------------- /src/pendulum/mixins/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-pendulum/pendulum/fc386be2623f711364c599df3e208eceb4dfa23b/src/pendulum/mixins/__init__.py -------------------------------------------------------------------------------- /src/pendulum/mixins/default.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from pendulum.formatting import Formatter 4 | 5 | 6 | _formatter = Formatter() 7 | 8 | 9 | class FormattableMixin: 10 | _formatter: Formatter = _formatter 11 | 12 | def format(self, fmt: str, locale: str | None = None) -> str: 13 | """ 14 | Formats the instance using the given format. 15 | 16 | :param fmt: The format to use 17 | :param locale: The locale to use 18 | """ 19 | return self._formatter.format(self, fmt, locale) 20 | 21 | def for_json(self) -> str: 22 | """ 23 | Methods for automatic json serialization by simplejson. 24 | """ 25 | return self.isoformat() 26 | 27 | def __format__(self, format_spec: str) -> str: 28 | if len(format_spec) > 0: 29 | if "%" in format_spec: 30 | return self.strftime(format_spec) 31 | 32 | return self.format(format_spec) 33 | 34 | return str(self) 35 | 36 | def __str__(self) -> str: 37 | return self.isoformat() 38 | -------------------------------------------------------------------------------- /src/pendulum/parsing/exceptions/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | 4 | class ParserError(ValueError): 5 | pass 6 | -------------------------------------------------------------------------------- /src/pendulum/py.typed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-pendulum/pendulum/fc386be2623f711364c599df3e208eceb4dfa23b/src/pendulum/py.typed -------------------------------------------------------------------------------- /src/pendulum/testing/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-pendulum/pendulum/fc386be2623f711364c599df3e208eceb4dfa23b/src/pendulum/testing/__init__.py -------------------------------------------------------------------------------- /src/pendulum/tz/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from functools import cache 4 | from zoneinfo import available_timezones 5 | 6 | from pendulum.tz.local_timezone import get_local_timezone 7 | from pendulum.tz.local_timezone import set_local_timezone 8 | from pendulum.tz.local_timezone import test_local_timezone 9 | from pendulum.tz.timezone import UTC 10 | from pendulum.tz.timezone import FixedTimezone 11 | from pendulum.tz.timezone import Timezone 12 | 13 | 14 | PRE_TRANSITION = "pre" 15 | POST_TRANSITION = "post" 16 | TRANSITION_ERROR = "error" 17 | 18 | _tz_cache: dict[int, FixedTimezone] = {} 19 | 20 | 21 | @cache 22 | def timezones() -> set[str]: 23 | return available_timezones() 24 | 25 | 26 | def fixed_timezone(offset: int) -> FixedTimezone: 27 | """ 28 | Return a Timezone instance given its offset in seconds. 29 | """ 30 | if offset in _tz_cache: 31 | return _tz_cache[offset] 32 | 33 | tz = FixedTimezone(offset) 34 | _tz_cache[offset] = tz 35 | 36 | return tz 37 | 38 | 39 | def local_timezone() -> Timezone | FixedTimezone: 40 | """ 41 | Return the local timezone. 42 | """ 43 | return get_local_timezone() 44 | 45 | 46 | __all__ = [ 47 | "UTC", 48 | "FixedTimezone", 49 | "Timezone", 50 | "fixed_timezone", 51 | "get_local_timezone", 52 | "local_timezone", 53 | "set_local_timezone", 54 | "test_local_timezone", 55 | "timezones", 56 | ] 57 | -------------------------------------------------------------------------------- /src/pendulum/tz/data/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-pendulum/pendulum/fc386be2623f711364c599df3e208eceb4dfa23b/src/pendulum/tz/data/__init__.py -------------------------------------------------------------------------------- /src/pendulum/tz/exceptions.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from typing import TYPE_CHECKING 4 | 5 | 6 | if TYPE_CHECKING: 7 | from datetime import datetime 8 | 9 | 10 | class TimezoneError(ValueError): 11 | pass 12 | 13 | 14 | class InvalidTimezone(TimezoneError): 15 | pass 16 | 17 | 18 | class NonExistingTime(TimezoneError): 19 | message = "The datetime {} does not exist." 20 | 21 | def __init__(self, dt: datetime) -> None: 22 | message = self.message.format(dt) 23 | 24 | super().__init__(message) 25 | 26 | 27 | class AmbiguousTime(TimezoneError): 28 | message = "The datetime {} is ambiguous." 29 | 30 | def __init__(self, dt: datetime) -> None: 31 | message = self.message.format(dt) 32 | 33 | super().__init__(message) 34 | -------------------------------------------------------------------------------- /src/pendulum/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-pendulum/pendulum/fc386be2623f711364c599df3e208eceb4dfa23b/src/pendulum/utils/__init__.py -------------------------------------------------------------------------------- /src/pendulum/utils/_compat.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import sys 4 | 5 | 6 | PYPY = hasattr(sys, "pypy_version_info") 7 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-pendulum/pendulum/fc386be2623f711364c599df3e208eceb4dfa23b/tests/__init__.py -------------------------------------------------------------------------------- /tests/benchmarks/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-pendulum/pendulum/fc386be2623f711364c599df3e208eceb4dfa23b/tests/benchmarks/__init__.py -------------------------------------------------------------------------------- /tests/benchmarks/test_parse_8601.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pytest 4 | 5 | from pendulum.parsing.iso8601 import parse_iso8601 6 | 7 | 8 | @pytest.mark.benchmark(group="Parsing") 9 | def test_parse_iso8601() -> None: 10 | # Date 11 | parse_iso8601("2016") 12 | parse_iso8601("2016-10") 13 | parse_iso8601("2016-10-06") 14 | parse_iso8601("20161006") 15 | 16 | # Time 17 | parse_iso8601("201610") 18 | 19 | # Datetime 20 | parse_iso8601("2016-10-06T12:34:56.123456") 21 | parse_iso8601("2016-10-06T12:34:56.123") 22 | parse_iso8601("2016-10-06T12:34:56.000123") 23 | parse_iso8601("2016-10-06T12") 24 | parse_iso8601("2016-10-06T123456") 25 | parse_iso8601("2016-10-06T123456.123456") 26 | parse_iso8601("20161006T123456.123456") 27 | parse_iso8601("20161006 123456.123456") 28 | 29 | # Datetime with offset 30 | parse_iso8601("2016-10-06T12:34:56.123456+05:30") 31 | parse_iso8601("2016-10-06T12:34:56.123456+0530") 32 | parse_iso8601("2016-10-06T12:34:56.123456-05:30") 33 | parse_iso8601("2016-10-06T12:34:56.123456-0530") 34 | parse_iso8601("2016-10-06T12:34:56.123456+05") 35 | parse_iso8601("2016-10-06T12:34:56.123456-05") 36 | parse_iso8601("20161006T123456,123456-05") 37 | parse_iso8601("2016-10-06T12:34:56.123456789+05:30") 38 | 39 | # Ordinal date 40 | parse_iso8601("2012-007") 41 | parse_iso8601("2012007") 42 | parse_iso8601("2017-079") 43 | 44 | # Week date 45 | parse_iso8601("2012-W05") 46 | parse_iso8601("2008-W39-6") 47 | parse_iso8601("2009-W53-7") 48 | parse_iso8601("2009-W01-1") 49 | 50 | # Week date wth time 51 | parse_iso8601("2008-W39-6T09") 52 | -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from typing import TYPE_CHECKING 4 | 5 | import pytest 6 | 7 | import pendulum 8 | 9 | 10 | if TYPE_CHECKING: 11 | from collections.abc import Iterator 12 | 13 | 14 | @pytest.fixture(autouse=True) 15 | def setup() -> Iterator[None]: 16 | pendulum.set_local_timezone(pendulum.timezone("America/Toronto")) 17 | 18 | yield 19 | 20 | pendulum.set_locale("en") 21 | pendulum.set_local_timezone() 22 | pendulum.week_starts_at(pendulum.WeekDay.MONDAY) 23 | pendulum.week_ends_at(pendulum.WeekDay.SUNDAY) 24 | 25 | 26 | def assert_datetime( 27 | d: pendulum.DateTime, 28 | year: int, 29 | month: int, 30 | day: int, 31 | hour: int | None = None, 32 | minute: int | None = None, 33 | second: int | None = None, 34 | microsecond: int | None = None, 35 | ) -> None: 36 | assert year == d.year 37 | assert month == d.month 38 | assert day == d.day 39 | 40 | if hour is not None: 41 | assert hour == d.hour 42 | 43 | if minute is not None: 44 | assert minute == d.minute 45 | 46 | if second is not None: 47 | assert second == d.second 48 | 49 | if microsecond is not None: 50 | assert microsecond == d.microsecond 51 | 52 | 53 | def assert_date(d: pendulum.Date, year: int, month: int, day: int) -> None: 54 | assert year == d.year 55 | assert month == d.month 56 | assert day == d.day 57 | 58 | 59 | def assert_time( 60 | t: pendulum.Time, 61 | hour: int, 62 | minute: int, 63 | second: int, 64 | microsecond: int | None = None, 65 | ) -> None: 66 | assert hour == t.hour 67 | assert minute == t.minute 68 | assert second == t.second 69 | 70 | if microsecond is not None: 71 | assert microsecond == t.microsecond 72 | 73 | 74 | def assert_duration( 75 | dur: pendulum.Duration, 76 | years: int | None = None, 77 | months: int | None = None, 78 | weeks: int | None = None, 79 | days: int | None = None, 80 | hours: int | None = None, 81 | minutes: int | None = None, 82 | seconds: int | None = None, 83 | microseconds: int | None = None, 84 | ) -> None: 85 | expected = {} 86 | actual = {} 87 | 88 | if years is not None: 89 | expected["years"] = dur.years 90 | actual["years"] = years 91 | 92 | if months is not None: 93 | expected["months"] = dur.months 94 | actual["months"] = months 95 | 96 | if weeks is not None: 97 | expected["weeks"] = dur.weeks 98 | actual["weeks"] = weeks 99 | 100 | if days is not None: 101 | expected["days"] = dur.remaining_days 102 | actual["days"] = days 103 | 104 | if hours is not None: 105 | expected["hours"] = dur.hours 106 | actual["hours"] = hours 107 | 108 | if minutes is not None: 109 | expected["minutes"] = dur.minutes 110 | actual["minutes"] = minutes 111 | 112 | if seconds is not None: 113 | expected["seconds"] = dur.remaining_seconds 114 | actual["seconds"] = seconds 115 | 116 | if microseconds is not None: 117 | expected["microseconds"] = dur.microseconds 118 | actual["microseconds"] = microseconds 119 | 120 | assert expected == actual 121 | -------------------------------------------------------------------------------- /tests/date/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-pendulum/pendulum/fc386be2623f711364c599df3e208eceb4dfa23b/tests/date/__init__.py -------------------------------------------------------------------------------- /tests/date/test_add.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from datetime import timedelta 4 | 5 | import pytest 6 | 7 | import pendulum 8 | 9 | from tests.conftest import assert_date 10 | 11 | 12 | def test_add_years_positive(): 13 | assert pendulum.date(1975, 1, 1).add(years=1).year == 1976 14 | 15 | 16 | def test_add_years_zero(): 17 | assert pendulum.date(1975, 1, 1).add(years=0).year == 1975 18 | 19 | 20 | def test_add_years_negative(): 21 | assert pendulum.date(1975, 1, 1).add(years=-1).year == 1974 22 | 23 | 24 | def test_add_months_positive(): 25 | assert pendulum.date(1975, 12, 1).add(months=1).month == 1 26 | 27 | 28 | def test_add_months_zero(): 29 | assert pendulum.date(1975, 12, 1).add(months=0).month == 12 30 | 31 | 32 | def test_add_months_negative(): 33 | assert pendulum.date(1975, 12, 1).add(months=-1).month == 11 34 | 35 | 36 | def test_add_month_with_overflow(): 37 | assert pendulum.Date(2012, 1, 31).add(months=1).month == 2 38 | 39 | 40 | def test_add_days_positive(): 41 | assert pendulum.Date(1975, 5, 31).add(days=1).day == 1 42 | 43 | 44 | def test_add_days_zero(): 45 | assert pendulum.Date(1975, 5, 31).add(days=0).day == 31 46 | 47 | 48 | def test_add_days_negative(): 49 | assert pendulum.Date(1975, 5, 31).add(days=-1).day == 30 50 | 51 | 52 | def test_add_weeks_positive(): 53 | assert pendulum.Date(1975, 5, 21).add(weeks=1).day == 28 54 | 55 | 56 | def test_add_weeks_zero(): 57 | assert pendulum.Date(1975, 5, 21).add(weeks=0).day == 21 58 | 59 | 60 | def test_add_weeks_negative(): 61 | assert pendulum.Date(1975, 5, 21).add(weeks=-1).day == 14 62 | 63 | 64 | def test_add_timedelta(): 65 | delta = timedelta(days=18) 66 | d = pendulum.date(2015, 3, 14) 67 | 68 | new = d + delta 69 | assert isinstance(new, pendulum.Date) 70 | assert_date(new, 2015, 4, 1) 71 | 72 | 73 | def test_add_duration(): 74 | duration = pendulum.duration(years=2, months=3, days=18) 75 | d = pendulum.Date(2015, 3, 14) 76 | 77 | new = d + duration 78 | assert_date(new, 2017, 7, 2) 79 | 80 | 81 | def test_addition_invalid_type(): 82 | d = pendulum.date(2015, 3, 14) 83 | 84 | with pytest.raises(TypeError): 85 | d + 3 86 | 87 | with pytest.raises(TypeError): 88 | 3 + d 89 | -------------------------------------------------------------------------------- /tests/date/test_behavior.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pickle 4 | 5 | from datetime import date 6 | 7 | import pytest 8 | 9 | import pendulum 10 | 11 | 12 | @pytest.fixture() 13 | def p(): 14 | return pendulum.Date(2016, 8, 27) 15 | 16 | 17 | @pytest.fixture() 18 | def d(): 19 | return date(2016, 8, 27) 20 | 21 | 22 | def test_timetuple(p, d): 23 | assert p.timetuple() == d.timetuple() 24 | 25 | 26 | def test_ctime(p, d): 27 | assert p.ctime() == d.ctime() 28 | 29 | 30 | def test_isoformat(p, d): 31 | assert p.isoformat() == d.isoformat() 32 | 33 | 34 | def test_toordinal(p, d): 35 | assert p.toordinal() == d.toordinal() 36 | 37 | 38 | def test_weekday(p, d): 39 | assert p.weekday() == d.weekday() 40 | 41 | 42 | def test_isoweekday(p, d): 43 | assert p.isoweekday() == d.isoweekday() 44 | 45 | 46 | def test_isocalendar(p, d): 47 | assert p.isocalendar() == d.isocalendar() 48 | 49 | 50 | def test_fromtimestamp(): 51 | assert pendulum.Date.fromtimestamp(0) == date.fromtimestamp(0) 52 | 53 | 54 | def test_fromordinal(): 55 | assert pendulum.Date.fromordinal(730120) == date.fromordinal(730120) 56 | 57 | 58 | def test_hash(): 59 | d1 = pendulum.Date(2016, 8, 27) 60 | d2 = pendulum.Date(2016, 8, 27) 61 | d3 = pendulum.Date(2016, 8, 28) 62 | 63 | assert hash(d2) == hash(d1) 64 | assert hash(d1) != hash(d3) 65 | 66 | 67 | def test_pickle(): 68 | d1 = pendulum.Date(2016, 8, 27) 69 | s = pickle.dumps(d1) 70 | d2 = pickle.loads(s) 71 | 72 | assert isinstance(d2, pendulum.Date) 73 | assert d2 == d1 74 | -------------------------------------------------------------------------------- /tests/date/test_construct.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from pendulum import Date 4 | from tests.conftest import assert_date 5 | 6 | 7 | def test_construct(): 8 | d = Date(2016, 10, 20) 9 | 10 | assert_date(d, 2016, 10, 20) 11 | 12 | 13 | def test_today(): 14 | d = Date.today() 15 | 16 | assert isinstance(d, Date) 17 | -------------------------------------------------------------------------------- /tests/date/test_fluent_setters.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pendulum 4 | 5 | from tests.conftest import assert_date 6 | 7 | 8 | def test_fluid_year_setter(): 9 | d = pendulum.Date(2016, 10, 20) 10 | new = d.set(year=1995) 11 | 12 | assert_date(new, 1995, 10, 20) 13 | assert new.year == 1995 14 | 15 | 16 | def test_fluid_month_setter(): 17 | d = pendulum.Date(2016, 7, 2) 18 | new = d.set(month=11) 19 | 20 | assert new.month == 11 21 | assert d.month == 7 22 | 23 | 24 | def test_fluid_day_setter(): 25 | d = pendulum.Date(2016, 7, 2) 26 | new = d.set(day=9) 27 | 28 | assert new.day == 9 29 | assert d.day == 2 30 | -------------------------------------------------------------------------------- /tests/date/test_getters.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pendulum 4 | 5 | 6 | def test_year(): 7 | d = pendulum.Date(1234, 5, 6) 8 | assert d.year == 1234 9 | 10 | 11 | def test_month(): 12 | d = pendulum.Date(1234, 5, 6) 13 | assert d.month == 5 14 | 15 | 16 | def test_day(): 17 | d = pendulum.Date(1234, 5, 6) 18 | assert d.day == 6 19 | 20 | 21 | def test_day_of_week(): 22 | d = pendulum.Date(2012, 5, 7) 23 | assert d.day_of_week == pendulum.MONDAY 24 | 25 | 26 | def test_day_of_year(): 27 | d = pendulum.Date(2015, 12, 31) 28 | assert d.day_of_year == 365 29 | d = pendulum.Date(2016, 12, 31) 30 | assert d.day_of_year == 366 31 | 32 | 33 | def test_days_in_month(): 34 | d = pendulum.Date(2012, 5, 7) 35 | assert d.days_in_month == 31 36 | 37 | 38 | def test_age(): 39 | d = pendulum.Date.today() 40 | assert d.age == 0 41 | assert d.add(years=1).age == -1 42 | assert d.subtract(years=1).age == 1 43 | 44 | 45 | def test_is_leap_year(): 46 | assert pendulum.Date(2012, 1, 1).is_leap_year() 47 | assert not pendulum.Date(2011, 1, 1).is_leap_year() 48 | 49 | 50 | def test_is_long_year(): 51 | assert pendulum.Date(2015, 1, 1).is_long_year() 52 | assert not pendulum.Date(2016, 1, 1).is_long_year() 53 | 54 | 55 | def test_week_of_month(): 56 | assert pendulum.Date(2012, 9, 30).week_of_month == 5 57 | assert pendulum.Date(2012, 9, 28).week_of_month == 5 58 | assert pendulum.Date(2012, 9, 20).week_of_month == 4 59 | assert pendulum.Date(2012, 9, 8).week_of_month == 2 60 | assert pendulum.Date(2012, 9, 1).week_of_month == 1 61 | assert pendulum.date(2020, 1, 1).week_of_month == 1 62 | assert pendulum.date(2020, 1, 7).week_of_month == 2 63 | assert pendulum.date(2020, 1, 14).week_of_month == 3 64 | 65 | 66 | def test_week_of_year_first_week(): 67 | assert pendulum.Date(2012, 1, 1).week_of_year == 52 68 | assert pendulum.Date(2012, 1, 2).week_of_year == 1 69 | 70 | 71 | def test_week_of_year_last_week(): 72 | assert pendulum.Date(2012, 12, 30).week_of_year == 52 73 | assert pendulum.Date(2012, 12, 31).week_of_year == 1 74 | 75 | 76 | def test_is_future(): 77 | d = pendulum.Date.today() 78 | assert not d.is_future() 79 | d = d.add(days=1) 80 | assert d.is_future() 81 | 82 | 83 | def test_is_past(): 84 | d = pendulum.Date.today() 85 | assert not d.is_past() 86 | d = d.subtract(days=1) 87 | assert d.is_past() 88 | -------------------------------------------------------------------------------- /tests/date/test_strings.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pendulum 4 | 5 | 6 | def test_to_string(): 7 | d = pendulum.Date(2016, 10, 16) 8 | assert str(d) == "2016-10-16" 9 | 10 | 11 | def test_to_date_string(): 12 | d = pendulum.Date(1975, 12, 25) 13 | assert d.to_date_string() == "1975-12-25" 14 | 15 | 16 | def test_to_formatted_date_string(): 17 | d = pendulum.Date(1975, 12, 25) 18 | assert d.to_formatted_date_string() == "Dec 25, 1975" 19 | 20 | 21 | def test_repr(): 22 | d = pendulum.Date(1975, 12, 25) 23 | 24 | assert repr(d) == "Date(1975, 12, 25)" 25 | assert d.__repr__() == "Date(1975, 12, 25)" 26 | 27 | 28 | def test_format_with_locale(): 29 | d = pendulum.Date(1975, 12, 25) 30 | expected = "jeudi 25e jour de décembre 1975" 31 | assert d.format("dddd Do [jour de] MMMM YYYY", locale="fr") == expected 32 | 33 | 34 | def test_strftime(): 35 | d = pendulum.Date(1975, 12, 25) 36 | assert d.strftime("%d") == "25" 37 | 38 | 39 | def test_for_json(): 40 | d = pendulum.Date(1975, 12, 25) 41 | assert d.for_json() == "1975-12-25" 42 | 43 | 44 | def test_format(): 45 | d = pendulum.Date(1975, 12, 25) 46 | assert f"{d}" == "1975-12-25" 47 | assert f"{d:YYYY}" == "1975" 48 | assert f"{d:%Y}" == "1975" 49 | -------------------------------------------------------------------------------- /tests/date/test_sub.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from datetime import datetime 4 | from datetime import timedelta 5 | 6 | import pytest 7 | 8 | import pendulum 9 | 10 | from tests.conftest import assert_date 11 | 12 | 13 | def test_subtract_years_positive(): 14 | assert pendulum.date(1975, 1, 1).subtract(years=1).year == 1974 15 | 16 | 17 | def test_subtract_years_zero(): 18 | assert pendulum.date(1975, 1, 1).subtract(years=0).year == 1975 19 | 20 | 21 | def test_subtract_years_negative(): 22 | assert pendulum.date(1975, 1, 1).subtract(years=-1).year == 1976 23 | 24 | 25 | def test_subtract_months_positive(): 26 | assert pendulum.date(1975, 1, 1).subtract(months=1).month == 12 27 | 28 | 29 | def test_subtract_months_zero(): 30 | assert pendulum.date(1975, 12, 1).subtract(months=0).month == 12 31 | 32 | 33 | def test_subtract_months_negative(): 34 | assert pendulum.date(1975, 11, 1).subtract(months=-1).month == 12 35 | 36 | 37 | def test_subtract_days_positive(): 38 | assert pendulum.Date(1975, 6, 1).subtract(days=1).day == 31 39 | 40 | 41 | def test_subtract_days_zero(): 42 | assert pendulum.Date(1975, 5, 31).subtract(days=0).day == 31 43 | 44 | 45 | def test_subtract_days_negative(): 46 | assert pendulum.Date(1975, 5, 30).subtract(days=-1).day == 31 47 | 48 | 49 | def test_subtract_days_max(): 50 | delta = pendulum.now() - pendulum.instance(datetime.min) 51 | assert pendulum.now().subtract(days=delta.days - 1).year == 1 52 | 53 | 54 | def test_subtract_weeks_positive(): 55 | assert pendulum.Date(1975, 5, 28).subtract(weeks=1).day == 21 56 | 57 | 58 | def test_subtract_weeks_zero(): 59 | assert pendulum.Date(1975, 5, 21).subtract(weeks=0).day == 21 60 | 61 | 62 | def test_subtract_weeks_negative(): 63 | assert pendulum.Date(1975, 5, 14).subtract(weeks=-1).day == 21 64 | 65 | 66 | def test_subtract_timedelta(): 67 | delta = timedelta(days=18) 68 | d = pendulum.date(2015, 3, 14) 69 | 70 | new = d - delta 71 | assert isinstance(new, pendulum.Date) 72 | assert_date(new, 2015, 2, 24) 73 | 74 | 75 | def test_subtract_duration(): 76 | delta = pendulum.duration(years=2, months=3, days=18) 77 | d = pendulum.date(2015, 3, 14) 78 | 79 | new = d - delta 80 | assert_date(new, 2012, 11, 26) 81 | 82 | 83 | def test_addition_invalid_type(): 84 | d = pendulum.date(2015, 3, 14) 85 | 86 | with pytest.raises(TypeError): 87 | d - "ab" 88 | 89 | with pytest.raises(TypeError): 90 | "ab" - d 91 | -------------------------------------------------------------------------------- /tests/datetime/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-pendulum/pendulum/fc386be2623f711364c599df3e208eceb4dfa23b/tests/datetime/__init__.py -------------------------------------------------------------------------------- /tests/datetime/test_create_from_timestamp.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pendulum 4 | 5 | from pendulum import timezone 6 | from tests.conftest import assert_datetime 7 | 8 | 9 | def test_create_from_timestamp_returns_pendulum(): 10 | d = pendulum.from_timestamp(pendulum.datetime(1975, 5, 21, 22, 32, 5).timestamp()) 11 | assert_datetime(d, 1975, 5, 21, 22, 32, 5) 12 | assert d.timezone_name == "UTC" 13 | 14 | 15 | def test_create_from_timestamp_with_timezone_string(): 16 | d = pendulum.from_timestamp(0, "America/Toronto") 17 | assert d.timezone_name == "America/Toronto" 18 | assert_datetime(d, 1969, 12, 31, 19, 0, 0) 19 | 20 | 21 | def test_create_from_timestamp_with_timezone(): 22 | d = pendulum.from_timestamp(0, timezone("America/Toronto")) 23 | assert d.timezone_name == "America/Toronto" 24 | assert_datetime(d, 1969, 12, 31, 19, 0, 0) 25 | -------------------------------------------------------------------------------- /tests/datetime/test_naive.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pendulum 4 | 5 | from tests.conftest import assert_datetime 6 | 7 | 8 | def test_naive(): 9 | dt = pendulum.naive(2018, 2, 2, 12, 34, 56, 123456) 10 | 11 | assert_datetime(dt, 2018, 2, 2, 12, 34, 56, 123456) 12 | assert dt.tzinfo is None 13 | assert dt.timezone is None 14 | assert dt.timezone_name is None 15 | 16 | 17 | def test_naive_add(): 18 | dt = pendulum.naive(2013, 3, 31, 1, 30) 19 | new = dt.add(hours=1) 20 | 21 | assert_datetime(new, 2013, 3, 31, 2, 30) 22 | 23 | 24 | def test_naive_subtract(): 25 | dt = pendulum.naive(2013, 3, 31, 1, 30) 26 | new = dt.subtract(hours=1) 27 | 28 | assert_datetime(new, 2013, 3, 31, 0, 30) 29 | 30 | 31 | def test_naive_in_timezone(): 32 | dt = pendulum.naive(2013, 3, 31, 1, 30) 33 | new = dt.in_timezone("Europe/Paris") 34 | 35 | assert_datetime(new, 2013, 3, 31, 1, 30) 36 | assert new.timezone_name == "Europe/Paris" 37 | 38 | 39 | def test_naive_in_timezone_dst(): 40 | dt = pendulum.naive(2013, 3, 31, 2, 30) 41 | new = dt.in_timezone("Europe/Paris") 42 | 43 | assert_datetime(new, 2013, 3, 31, 3, 30) 44 | assert new.timezone_name == "Europe/Paris" 45 | 46 | 47 | def test_add(): 48 | dt = pendulum.naive(2013, 3, 31, 2, 30) 49 | new = dt.add(days=3) 50 | 51 | assert_datetime(new, 2013, 4, 3, 2, 30) 52 | 53 | 54 | def test_subtract(): 55 | dt = pendulum.naive(2013, 3, 31, 2, 30) 56 | new = dt.subtract(days=3) 57 | 58 | assert_datetime(new, 2013, 3, 28, 2, 30) 59 | 60 | 61 | def test_to_strings(): 62 | dt = pendulum.naive(2013, 3, 31, 2, 30) 63 | 64 | assert dt.isoformat() == "2013-03-31T02:30:00" 65 | assert dt.to_iso8601_string() == "2013-03-31T02:30:00" 66 | assert dt.to_rfc3339_string() == "2013-03-31T02:30:00" 67 | assert dt.to_atom_string() == "2013-03-31T02:30:00" 68 | assert dt.to_cookie_string() == "Sunday, 31-Mar-2013 02:30:00 " 69 | 70 | 71 | def test_naive_method(): 72 | dt = pendulum.datetime(2018, 2, 2, 12, 34, 56, 123456) 73 | dt = dt.naive() 74 | 75 | assert_datetime(dt, 2018, 2, 2, 12, 34, 56, 123456) 76 | assert dt.tzinfo is None 77 | assert dt.timezone is None 78 | assert dt.timezone_name is None 79 | -------------------------------------------------------------------------------- /tests/datetime/test_replace.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pendulum 4 | 5 | from tests.conftest import assert_datetime 6 | 7 | 8 | def test_replace_tzinfo_dst_off(): 9 | utc = pendulum.datetime(2016, 3, 27, 0, 30) # 30 min before DST turning on 10 | in_paris = utc.in_tz("Europe/Paris") 11 | 12 | assert_datetime(in_paris, 2016, 3, 27, 1, 30, 0) 13 | 14 | in_paris = in_paris.replace(second=1) 15 | 16 | assert_datetime(in_paris, 2016, 3, 27, 1, 30, 1) 17 | assert not in_paris.is_dst() 18 | assert in_paris.offset == 3600 19 | assert in_paris.timezone_name == "Europe/Paris" 20 | 21 | 22 | def test_replace_tzinfo_dst_transitioning_on(): 23 | utc = pendulum.datetime(2016, 3, 27, 1, 30) # In middle of turning on 24 | in_paris = utc.in_tz("Europe/Paris") 25 | 26 | assert_datetime(in_paris, 2016, 3, 27, 3, 30, 0) 27 | 28 | in_paris = in_paris.replace(second=1) 29 | 30 | assert_datetime(in_paris, 2016, 3, 27, 3, 30, 1) 31 | assert in_paris.is_dst() 32 | assert in_paris.offset == 7200 33 | assert in_paris.timezone_name == "Europe/Paris" 34 | 35 | 36 | def test_replace_tzinfo_dst_on(): 37 | utc = pendulum.datetime(2016, 10, 30, 0, 30) # 30 min before DST turning off 38 | in_paris = utc.in_tz("Europe/Paris") 39 | 40 | assert_datetime(in_paris, 2016, 10, 30, 2, 30, 0) 41 | 42 | in_paris = in_paris.replace(second=1) 43 | 44 | assert_datetime(in_paris, 2016, 10, 30, 2, 30, 1) 45 | assert in_paris.is_dst() 46 | assert in_paris.offset == 7200 47 | assert in_paris.timezone_name == "Europe/Paris" 48 | 49 | 50 | def test_replace_tzinfo_dst_transitioning_off(): 51 | utc = pendulum.datetime(2016, 10, 30, 1, 30) # In the middle of turning off 52 | in_paris = utc.in_tz("Europe/Paris") 53 | 54 | assert_datetime(in_paris, 2016, 10, 30, 2, 30, 0) 55 | 56 | in_paris = in_paris.replace(second=1) 57 | 58 | assert_datetime(in_paris, 2016, 10, 30, 2, 30, 1) 59 | assert not in_paris.is_dst() 60 | assert in_paris.offset == 3600 61 | assert in_paris.timezone_name == "Europe/Paris" 62 | -------------------------------------------------------------------------------- /tests/datetime/test_timezone.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pendulum 4 | 5 | from tests.conftest import assert_datetime 6 | 7 | 8 | def test_in_timezone(): 9 | d = pendulum.datetime(2015, 1, 15, 18, 15, 34) 10 | now = pendulum.datetime(2015, 1, 15, 18, 15, 34) 11 | assert d.timezone_name == "UTC" 12 | assert_datetime(d, now.year, now.month, now.day, now.hour, now.minute) 13 | 14 | d = d.in_timezone("Europe/Paris") 15 | assert d.timezone_name == "Europe/Paris" 16 | assert_datetime(d, now.year, now.month, now.day, now.hour + 1, now.minute) 17 | 18 | 19 | def test_in_tz(): 20 | d = pendulum.datetime(2015, 1, 15, 18, 15, 34) 21 | now = pendulum.datetime(2015, 1, 15, 18, 15, 34) 22 | assert d.timezone_name == "UTC" 23 | assert_datetime(d, now.year, now.month, now.day, now.hour, now.minute) 24 | 25 | d = d.in_tz("Europe/Paris") 26 | assert d.timezone_name == "Europe/Paris" 27 | assert_datetime(d, now.year, now.month, now.day, now.hour + 1, now.minute) 28 | 29 | 30 | def test_astimezone(): 31 | d = pendulum.datetime(2015, 1, 15, 18, 15, 34) 32 | now = pendulum.datetime(2015, 1, 15, 18, 15, 34) 33 | assert d.timezone_name == "UTC" 34 | assert_datetime(d, now.year, now.month, now.day, now.hour, now.minute) 35 | 36 | d = d.astimezone(pendulum.timezone("Europe/Paris")) 37 | assert d.timezone_name == "Europe/Paris" 38 | assert_datetime(d, now.year, now.month, now.day, now.hour + 1, now.minute) 39 | -------------------------------------------------------------------------------- /tests/duration/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-pendulum/pendulum/fc386be2623f711364c599df3e208eceb4dfa23b/tests/duration/__init__.py -------------------------------------------------------------------------------- /tests/duration/test_add_sub.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from datetime import timedelta 4 | 5 | import pendulum 6 | 7 | from tests.conftest import assert_duration 8 | 9 | 10 | def test_add_interval(): 11 | p1 = pendulum.duration(days=23, seconds=32) 12 | p2 = pendulum.duration(days=12, seconds=30) 13 | 14 | p = p1 + p2 15 | assert_duration(p, 0, 0, 5, 0, 0, 1, 2) 16 | 17 | 18 | def test_add_timedelta(): 19 | p1 = pendulum.duration(days=23, seconds=32) 20 | p2 = timedelta(days=12, seconds=30) 21 | 22 | p = p1 + p2 23 | assert_duration(p, 0, 0, 5, 0, 0, 1, 2) 24 | 25 | 26 | def test_add_unsupported(): 27 | p = pendulum.duration(days=23, seconds=32) 28 | assert NotImplemented == p.__add__(5) 29 | 30 | 31 | def test_sub_interval(): 32 | p1 = pendulum.duration(days=23, seconds=32) 33 | p2 = pendulum.duration(days=12, seconds=28) 34 | 35 | p = p1 - p2 36 | assert_duration(p, 0, 0, 1, 4, 0, 0, 4) 37 | 38 | 39 | def test_sub_timedelta(): 40 | p1 = pendulum.duration(days=23, seconds=32) 41 | p2 = timedelta(days=12, seconds=28) 42 | 43 | p = p1 - p2 44 | assert_duration(p, 0, 0, 1, 4, 0, 0, 4) 45 | 46 | 47 | def test_sub_unsupported(): 48 | p = pendulum.duration(days=23, seconds=32) 49 | assert NotImplemented == p.__sub__(5) 50 | 51 | 52 | def test_neg(): 53 | p = pendulum.duration(days=23, seconds=32) 54 | assert_duration(-p, 0, 0, -3, -2, 0, 0, -32) 55 | -------------------------------------------------------------------------------- /tests/duration/test_arithmetic.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pendulum 4 | 5 | from tests.conftest import assert_duration 6 | 7 | 8 | def test_multiply(): 9 | it = pendulum.duration(days=6, seconds=34, microseconds=522222) 10 | mul = it * 2 11 | 12 | assert isinstance(mul, pendulum.Duration) 13 | assert_duration(mul, 0, 0, 1, 5, 0, 1, 9, 44444) 14 | 15 | it = pendulum.duration(days=6, seconds=34, microseconds=522222) 16 | mul = 2 * it 17 | 18 | assert isinstance(mul, pendulum.Duration) 19 | assert_duration(mul, 0, 0, 1, 5, 0, 1, 9, 44444) 20 | 21 | it = pendulum.duration( 22 | years=2, months=3, weeks=4, days=6, seconds=34, microseconds=522222 23 | ) 24 | mul = 2 * it 25 | 26 | assert isinstance(mul, pendulum.Duration) 27 | assert_duration(mul, 4, 6, 9, 5, 0, 1, 9, 44444) 28 | 29 | 30 | def test_divide(): 31 | it = pendulum.duration(days=2, seconds=34, microseconds=522222) 32 | mul = it / 2 33 | 34 | assert isinstance(mul, pendulum.Duration) 35 | assert_duration(mul, 0, 0, 0, 1, 0, 0, 17, 261111) 36 | 37 | it = pendulum.duration(days=2, seconds=35, microseconds=522222) 38 | mul = it / 2 39 | 40 | assert isinstance(mul, pendulum.Duration) 41 | assert_duration(mul, 0, 0, 0, 1, 0, 0, 17, 761111) 42 | 43 | it = pendulum.duration(days=2, seconds=35, microseconds=522222) 44 | mul = it / 1.1 45 | 46 | assert isinstance(mul, pendulum.Duration) 47 | assert_duration(mul, 0, 0, 0, 1, 19, 38, 43, 202020) 48 | 49 | it = pendulum.duration(years=2, months=4, days=2, seconds=35, microseconds=522222) 50 | mul = it / 2 51 | 52 | assert isinstance(mul, pendulum.Duration) 53 | assert_duration(mul, 1, 2, 0, 1, 0, 0, 17, 761111) 54 | 55 | it = pendulum.duration(years=2, months=4, days=2, seconds=35, microseconds=522222) 56 | mul = it / 2.0 57 | 58 | assert isinstance(mul, pendulum.Duration) 59 | assert_duration(mul, 1, 2, 0, 1, 0, 0, 17, 761111) 60 | 61 | 62 | def test_floor_divide(): 63 | it = pendulum.duration(days=2, seconds=34, microseconds=522222) 64 | mul = it // 2 65 | 66 | assert isinstance(mul, pendulum.Duration) 67 | assert_duration(mul, 0, 0, 0, 1, 0, 0, 17, 261111) 68 | 69 | it = pendulum.duration(days=2, seconds=35, microseconds=522222) 70 | mul = it // 3 71 | 72 | assert isinstance(mul, pendulum.Duration) 73 | assert_duration(mul, 0, 0, 0, 0, 16, 0, 11, 840740) 74 | 75 | it = pendulum.duration(years=2, months=4, days=2, seconds=34, microseconds=522222) 76 | mul = it // 2 77 | 78 | assert isinstance(mul, pendulum.Duration) 79 | assert_duration(mul, 1, 2, 0, 1, 0, 0, 17, 261111) 80 | 81 | it = pendulum.duration(years=2, months=4, days=2, seconds=35, microseconds=522222) 82 | mul = it // 3 83 | 84 | assert isinstance(mul, pendulum.Duration) 85 | assert_duration(mul, 0, 1, 0, 0, 16, 0, 11, 840740) 86 | -------------------------------------------------------------------------------- /tests/duration/test_behavior.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pickle 4 | 5 | from copy import deepcopy 6 | from datetime import timedelta 7 | 8 | import pendulum 9 | 10 | from tests.conftest import assert_duration 11 | 12 | 13 | def test_pickle() -> None: 14 | it = pendulum.duration(days=3, seconds=2456, microseconds=123456) 15 | s = pickle.dumps(it) 16 | it2 = pickle.loads(s) 17 | 18 | assert it == it2 19 | 20 | 21 | def test_comparison_to_timedelta() -> None: 22 | duration = pendulum.duration(days=3) 23 | 24 | assert duration < timedelta(days=4) 25 | 26 | 27 | def test_deepcopy() -> None: 28 | duration = pendulum.duration(months=1) 29 | copied_duration = deepcopy(duration) 30 | 31 | assert copied_duration == duration 32 | assert_duration(copied_duration, months=1) 33 | -------------------------------------------------------------------------------- /tests/duration/test_construct.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from datetime import timedelta 4 | 5 | import pytest 6 | 7 | import pendulum 8 | 9 | from pendulum.duration import AbsoluteDuration 10 | from tests.conftest import assert_duration 11 | 12 | 13 | def test_defaults(): 14 | pi = pendulum.duration() 15 | assert_duration(pi, 0, 0, 0, 0, 0, 0, 0) 16 | 17 | 18 | def test_years(): 19 | pi = pendulum.duration(years=2) 20 | assert_duration(pi, years=2, weeks=0) 21 | assert pi.days == 730 22 | assert pi.total_seconds() == 63072000 23 | 24 | 25 | def test_months(): 26 | pi = pendulum.duration(months=3) 27 | assert_duration(pi, months=3, weeks=0) 28 | assert pi.days == 90 29 | assert pi.total_seconds() == 7776000 30 | 31 | 32 | def test_weeks(): 33 | pi = pendulum.duration(days=365) 34 | assert_duration(pi, weeks=52) 35 | 36 | pi = pendulum.duration(days=13) 37 | assert_duration(pi, weeks=1) 38 | 39 | 40 | def test_days(): 41 | pi = pendulum.duration(days=6) 42 | assert_duration(pi, 0, 0, 0, 6, 0, 0, 0) 43 | 44 | pi = pendulum.duration(days=16) 45 | assert_duration(pi, 0, 0, 2, 2, 0, 0, 0) 46 | 47 | 48 | def test_hours(): 49 | pi = pendulum.duration(seconds=3600 * 3) 50 | assert_duration(pi, 0, 0, 0, 0, 3, 0, 0) 51 | 52 | 53 | def test_minutes(): 54 | pi = pendulum.duration(seconds=60 * 3) 55 | assert_duration(pi, 0, 0, 0, 0, 0, 3, 0) 56 | 57 | pi = pendulum.duration(seconds=60 * 3 + 12) 58 | assert_duration(pi, 0, 0, 0, 0, 0, 3, 12) 59 | 60 | 61 | def test_all(): 62 | pi = pendulum.duration( 63 | years=2, months=3, days=1177, seconds=7284, microseconds=1000000 64 | ) 65 | assert_duration(pi, 2, 3, 168, 1, 2, 1, 25) 66 | assert pi.days == 1997 67 | assert pi.seconds == 7285 68 | 69 | 70 | def test_absolute_interval(): 71 | pi = AbsoluteDuration(days=-1177, seconds=-7284, microseconds=-1000001) 72 | assert_duration(pi, 0, 0, 168, 1, 2, 1, 25) 73 | assert pi.microseconds == 1 74 | assert pi.invert 75 | 76 | 77 | def test_invert(): 78 | pi = pendulum.duration(days=1177, seconds=7284, microseconds=1000000) 79 | assert not pi.invert 80 | 81 | pi = pendulum.duration(days=-1177, seconds=-7284, microseconds=-1000000) 82 | assert pi.invert 83 | 84 | 85 | def test_as_timedelta(): 86 | pi = pendulum.duration(seconds=3456.123456) 87 | assert_duration(pi, 0, 0, 0, 0, 0, 57, 36, 123456) 88 | delta = pi.as_timedelta() 89 | assert isinstance(delta, timedelta) 90 | assert delta.total_seconds() == 3456.123456 91 | assert delta.seconds == 3456 92 | 93 | 94 | def test_float_years_and_months(): 95 | with pytest.raises(ValueError): 96 | pendulum.duration(years=1.5) 97 | 98 | with pytest.raises(ValueError): 99 | pendulum.duration(months=1.5) 100 | -------------------------------------------------------------------------------- /tests/duration/test_in_methods.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pendulum 4 | 5 | 6 | def test_in_weeks(): 7 | it = pendulum.duration(days=17) 8 | assert it.in_weeks() == 2 9 | 10 | 11 | def test_in_days(): 12 | it = pendulum.duration(days=3) 13 | assert it.in_days() == 3 14 | 15 | 16 | def test_in_hours(): 17 | it = pendulum.duration(days=3, minutes=72) 18 | assert it.in_hours() == 73 19 | 20 | 21 | def test_in_minutes(): 22 | it = pendulum.duration(minutes=6, seconds=72) 23 | assert it.in_minutes() == 7 24 | 25 | 26 | def test_in_seconds(): 27 | it = pendulum.duration(seconds=72) 28 | assert it.in_seconds() == 72 29 | -------------------------------------------------------------------------------- /tests/duration/test_in_words.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pendulum 4 | 5 | 6 | def test_week(): 7 | assert pendulum.duration(days=364).in_words() == "52 weeks" 8 | assert pendulum.duration(days=7).in_words() == "1 week" 9 | 10 | 11 | def test_week_to_string(): 12 | assert str(pendulum.duration(days=364)) == "52 weeks" 13 | assert str(pendulum.duration(days=7)) == "1 week" 14 | 15 | 16 | def test_weeks_and_day(): 17 | assert pendulum.duration(days=365).in_words() == "52 weeks 1 day" 18 | 19 | 20 | def test_all(): 21 | pi = pendulum.duration( 22 | years=2, months=3, days=1177, seconds=7284, microseconds=1000000 23 | ) 24 | 25 | expected = "2 years 3 months 168 weeks 1 day 2 hours 1 minute 25 seconds" 26 | assert pi.in_words() == expected 27 | 28 | 29 | def test_in_french(): 30 | pi = pendulum.duration( 31 | years=2, months=3, days=1177, seconds=7284, microseconds=1000000 32 | ) 33 | 34 | expected = "2 ans 3 mois 168 semaines 1 jour 2 heures 1 minute 25 secondes" 35 | assert pi.in_words(locale="fr") == expected 36 | 37 | 38 | def test_repr(): 39 | pi = pendulum.duration( 40 | years=2, months=3, days=1177, seconds=7284, microseconds=1000000 41 | ) 42 | 43 | expected = ( 44 | "Duration(years=2, months=3, weeks=168, days=1, hours=2, minutes=1, seconds=25)" 45 | ) 46 | assert repr(pi) == expected 47 | 48 | 49 | def test_singular_negative_values(): 50 | pi = pendulum.duration(days=-1) 51 | 52 | assert pi.in_words() == "-1 day" 53 | 54 | 55 | def test_separator(): 56 | pi = pendulum.duration(days=1177, seconds=7284, microseconds=1000000) 57 | 58 | expected = "168 weeks, 1 day, 2 hours, 1 minute, 25 seconds" 59 | assert pi.in_words(separator=", ") == expected 60 | 61 | 62 | def test_subseconds(): 63 | pi = pendulum.duration(microseconds=123456) 64 | 65 | assert pi.in_words() == "0.12 second" 66 | 67 | 68 | def test_subseconds_with_seconds(): 69 | pi = pendulum.duration(seconds=12, microseconds=123456) 70 | 71 | assert pi.in_words() == "12 seconds" 72 | 73 | 74 | def test_duration_with_all_zero_values(): 75 | pi = pendulum.duration() 76 | 77 | assert pi.in_words() == "0 microseconds" 78 | -------------------------------------------------------------------------------- /tests/duration/test_total_methods.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pendulum 4 | 5 | 6 | def test_in_weeks(): 7 | it = pendulum.duration(days=17) 8 | assert round(it.total_weeks(), 2) == 2.43 9 | 10 | 11 | def test_in_days(): 12 | it = pendulum.duration(days=3) 13 | assert it.total_days() == 3 14 | 15 | 16 | def test_in_hours(): 17 | it = pendulum.duration(days=3, minutes=72) 18 | assert it.total_hours() == 73.2 19 | 20 | 21 | def test_in_minutes(): 22 | it = pendulum.duration(minutes=6, seconds=72) 23 | assert it.total_minutes() == 7.2 24 | 25 | 26 | def test_in_seconds(): 27 | it = pendulum.duration(seconds=72, microseconds=123456) 28 | assert it.total_seconds() == 72.123456 29 | -------------------------------------------------------------------------------- /tests/fixtures/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-pendulum/pendulum/fc386be2623f711364c599df3e208eceb4dfa23b/tests/fixtures/__init__.py -------------------------------------------------------------------------------- /tests/fixtures/tz/Paris: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-pendulum/pendulum/fc386be2623f711364c599df3e208eceb4dfa23b/tests/fixtures/tz/Paris -------------------------------------------------------------------------------- /tests/fixtures/tz/clock/etc/sysconfig/clock: -------------------------------------------------------------------------------- 1 | ZONE="/usr/share/zoneinfo/Europe/Zurich" 2 | -------------------------------------------------------------------------------- /tests/fixtures/tz/symlink/etc/localtime: -------------------------------------------------------------------------------- 1 | ../usr/share/zoneinfo/Europe/Paris -------------------------------------------------------------------------------- /tests/fixtures/tz/symlink/usr/share/zoneinfo/Europe/Paris: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-pendulum/pendulum/fc386be2623f711364c599df3e208eceb4dfa23b/tests/fixtures/tz/symlink/usr/share/zoneinfo/Europe/Paris -------------------------------------------------------------------------------- /tests/fixtures/tz/timezone_dir/etc/localtime: -------------------------------------------------------------------------------- 1 | ../usr/share/zoneinfo/Europe/Paris -------------------------------------------------------------------------------- /tests/fixtures/tz/timezone_dir/etc/timezone/blank.md: -------------------------------------------------------------------------------- 1 | # Blank file 2 | 3 | Necessary for environments, which not handle empty folder synchronization well, to be sure the parent folder is created. 4 | 5 | The `/etc/timezone` folder is necessary to ensure that the package not fail if that folder exists. 6 | -------------------------------------------------------------------------------- /tests/fixtures/tz/timezone_dir/usr/share/zoneinfo/Europe/Paris: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-pendulum/pendulum/fc386be2623f711364c599df3e208eceb4dfa23b/tests/fixtures/tz/timezone_dir/usr/share/zoneinfo/Europe/Paris -------------------------------------------------------------------------------- /tests/formatting/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-pendulum/pendulum/fc386be2623f711364c599df3e208eceb4dfa23b/tests/formatting/__init__.py -------------------------------------------------------------------------------- /tests/helpers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-pendulum/pendulum/fc386be2623f711364c599df3e208eceb4dfa23b/tests/helpers/__init__.py -------------------------------------------------------------------------------- /tests/helpers/test_local_time.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pendulum 4 | 5 | from pendulum.helpers import local_time 6 | 7 | 8 | def test_local_time_positive_integer(): 9 | d = pendulum.datetime(2016, 8, 7, 12, 34, 56, 123456) 10 | 11 | t = local_time(d.int_timestamp, 0, d.microsecond) 12 | assert d.year == t[0] 13 | assert d.month == t[1] 14 | assert d.day == t[2] 15 | assert d.hour == t[3] 16 | assert d.minute == t[4] 17 | assert d.second == t[5] 18 | assert d.microsecond == t[6] 19 | 20 | 21 | def test_local_time_negative_integer(): 22 | d = pendulum.datetime(1951, 8, 7, 12, 34, 56, 123456) 23 | 24 | t = local_time(d.int_timestamp, 0, d.microsecond) 25 | assert d.year == t[0] 26 | assert d.month == t[1] 27 | assert d.day == t[2] 28 | assert d.hour == t[3] 29 | assert d.minute == t[4] 30 | assert d.second == t[5] 31 | assert d.microsecond == t[6] 32 | -------------------------------------------------------------------------------- /tests/interval/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-pendulum/pendulum/fc386be2623f711364c599df3e208eceb4dfa23b/tests/interval/__init__.py -------------------------------------------------------------------------------- /tests/interval/test_add_subtract.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pendulum 4 | 5 | 6 | def test_dst_add(): 7 | start = pendulum.datetime(2017, 3, 7, tz="America/Toronto") 8 | end = start.add(days=6) 9 | interval = end - start 10 | new_end = start + interval 11 | 12 | assert new_end == end 13 | 14 | 15 | def test_dst_add_non_variable_units(): 16 | start = pendulum.datetime(2013, 3, 31, 1, 30, tz="Europe/Paris") 17 | end = start.add(hours=1) 18 | interval = end - start 19 | new_end = start + interval 20 | 21 | assert new_end == end 22 | 23 | 24 | def test_dst_subtract(): 25 | start = pendulum.datetime(2017, 3, 7, tz="America/Toronto") 26 | end = start.add(days=6) 27 | interval = end - start 28 | new_start = end - interval 29 | 30 | assert new_start == start 31 | 32 | 33 | def test_naive_subtract(): 34 | start = pendulum.naive(2013, 3, 31, 1, 30) 35 | end = start.add(hours=1) 36 | interval = end - start 37 | new_end = start + interval 38 | 39 | assert new_end == end 40 | 41 | 42 | def test_negative_difference_subtract(): 43 | start = pendulum.datetime(2018, 5, 28, 12, 34, 56, 123456) 44 | end = pendulum.datetime(2018, 1, 1) 45 | 46 | interval = end - start 47 | new_end = start + interval 48 | 49 | assert new_end == end 50 | -------------------------------------------------------------------------------- /tests/interval/test_arithmetic.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pendulum 4 | 5 | from tests.conftest import assert_duration 6 | 7 | 8 | def test_multiply(): 9 | dt1 = pendulum.DateTime(2016, 8, 7, 12, 34, 56) 10 | dt2 = dt1.add(days=6, seconds=34) 11 | it = pendulum.interval(dt1, dt2) 12 | mul = it * 2 13 | assert isinstance(mul, pendulum.Duration) 14 | assert_duration(mul, 0, 0, 1, 5, 0, 1, 8) 15 | 16 | dt1 = pendulum.DateTime(2016, 8, 7, 12, 34, 56) 17 | dt2 = dt1.add(days=6, seconds=34) 18 | it = pendulum.interval(dt1, dt2) 19 | mul = it * 2 20 | assert isinstance(mul, pendulum.Duration) 21 | assert_duration(mul, 0, 0, 1, 5, 0, 1, 8) 22 | 23 | 24 | def test_divide(): 25 | dt1 = pendulum.DateTime(2016, 8, 7, 12, 34, 56) 26 | dt2 = dt1.add(days=2, seconds=34) 27 | it = pendulum.interval(dt1, dt2) 28 | mul = it / 2 29 | assert isinstance(mul, pendulum.Duration) 30 | assert_duration(mul, 0, 0, 0, 1, 0, 0, 17) 31 | 32 | dt1 = pendulum.DateTime(2016, 8, 7, 12, 34, 56) 33 | dt2 = dt1.add(days=2, seconds=35) 34 | it = pendulum.interval(dt1, dt2) 35 | mul = it / 2 36 | assert isinstance(mul, pendulum.Duration) 37 | assert_duration(mul, 0, 0, 0, 1, 0, 0, 17) 38 | 39 | 40 | def test_floor_divide(): 41 | dt1 = pendulum.DateTime(2016, 8, 7, 12, 34, 56) 42 | dt2 = dt1.add(days=2, seconds=34) 43 | it = pendulum.interval(dt1, dt2) 44 | mul = it // 2 45 | assert isinstance(mul, pendulum.Duration) 46 | assert_duration(mul, 0, 0, 0, 1, 0, 0, 17) 47 | 48 | dt1 = pendulum.DateTime(2016, 8, 7, 12, 34, 56) 49 | dt2 = dt1.add(days=2, seconds=35) 50 | it = pendulum.interval(dt1, dt2) 51 | mul = it // 3 52 | assert isinstance(mul, pendulum.Duration) 53 | assert_duration(mul, 0, 0, 0, 0, 16, 0, 11) 54 | -------------------------------------------------------------------------------- /tests/interval/test_behavior.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pickle 4 | 5 | from datetime import timedelta 6 | 7 | import pendulum 8 | 9 | 10 | def test_pickle(): 11 | dt1 = pendulum.datetime(2016, 11, 18) 12 | dt2 = pendulum.datetime(2016, 11, 20) 13 | 14 | p = pendulum.interval(dt1, dt2) 15 | s = pickle.dumps(p) 16 | p2 = pickle.loads(s) 17 | 18 | assert p.start == p2.start 19 | assert p.end == p2.end 20 | assert p.invert == p2.invert 21 | 22 | p = pendulum.interval(dt2, dt1) 23 | s = pickle.dumps(p) 24 | p2 = pickle.loads(s) 25 | 26 | assert p.start == p2.start 27 | assert p.end == p2.end 28 | assert p.invert == p2.invert 29 | 30 | p = pendulum.interval(dt2, dt1, True) 31 | s = pickle.dumps(p) 32 | p2 = pickle.loads(s) 33 | 34 | assert p.start == p2.start 35 | assert p.end == p2.end 36 | assert p.invert == p2.invert 37 | 38 | 39 | def test_comparison_to_timedelta(): 40 | dt1 = pendulum.datetime(2016, 11, 18) 41 | dt2 = pendulum.datetime(2016, 11, 20) 42 | 43 | interval = dt2 - dt1 44 | 45 | assert interval < timedelta(days=4) 46 | 47 | 48 | def test_equality_to_timedelta(): 49 | dt1 = pendulum.datetime(2016, 11, 18) 50 | dt2 = pendulum.datetime(2016, 11, 20) 51 | 52 | interval = dt2 - dt1 53 | 54 | assert interval == timedelta(days=2) 55 | 56 | 57 | def test_inequality(): 58 | dt1 = pendulum.datetime(2016, 11, 18) 59 | dt2 = pendulum.datetime(2016, 11, 20) 60 | dt3 = pendulum.datetime(2016, 11, 22) 61 | 62 | interval1 = dt2 - dt1 63 | interval2 = dt3 - dt2 64 | interval3 = dt3 - dt1 65 | 66 | assert interval1 != interval2 67 | assert interval1 != interval3 68 | -------------------------------------------------------------------------------- /tests/interval/test_construct.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from datetime import datetime 4 | 5 | import pendulum 6 | 7 | from tests.conftest import assert_datetime 8 | 9 | 10 | def test_with_datetimes(): 11 | dt1 = datetime(2000, 1, 1) 12 | dt2 = datetime(2000, 1, 31) 13 | p = pendulum.interval(dt1, dt2) 14 | 15 | assert isinstance(p.start, pendulum.DateTime) 16 | assert isinstance(p.end, pendulum.DateTime) 17 | assert_datetime(p.start, 2000, 1, 1) 18 | assert_datetime(p.end, 2000, 1, 31) 19 | 20 | 21 | def test_with_pendulum(): 22 | dt1 = pendulum.DateTime(2000, 1, 1) 23 | dt2 = pendulum.DateTime(2000, 1, 31) 24 | p = pendulum.interval(dt1, dt2) 25 | 26 | assert_datetime(p.start, 2000, 1, 1) 27 | assert_datetime(p.end, 2000, 1, 31) 28 | 29 | 30 | def test_inverted(): 31 | dt1 = pendulum.DateTime(2000, 1, 1) 32 | dt2 = pendulum.DateTime(2000, 1, 31) 33 | p = pendulum.interval(dt2, dt1) 34 | 35 | assert_datetime(p.start, 2000, 1, 31) 36 | assert_datetime(p.end, 2000, 1, 1) 37 | 38 | 39 | def test_inverted_and_absolute(): 40 | dt1 = pendulum.DateTime(2000, 1, 1) 41 | dt2 = pendulum.DateTime(2000, 1, 31) 42 | p = pendulum.interval(dt2, dt1, True) 43 | 44 | assert_datetime(p.start, 2000, 1, 1) 45 | assert_datetime(p.end, 2000, 1, 31) 46 | 47 | 48 | def test_accuracy(): 49 | dt1 = pendulum.DateTime(2000, 11, 20) 50 | dt2 = pendulum.DateTime(2000, 11, 25) 51 | dt3 = pendulum.DateTime(2016, 11, 5) 52 | p1 = pendulum.interval(dt1, dt3) 53 | p2 = pendulum.interval(dt2, dt3) 54 | 55 | assert p1.years == 15 56 | assert p1.in_years() == 15 57 | assert p1.months == 11 58 | assert p1.in_months() == 191 59 | assert p1.days == 5829 60 | assert p1.remaining_days == 2 61 | assert p1.in_days() == 5829 62 | 63 | assert p2.years == 15 64 | assert p2.in_years() == 15 65 | assert p2.months == 11 66 | assert p2.in_months() == 191 67 | assert p2.days == 5824 68 | assert p2.remaining_days == 4 69 | assert p2.in_days() == 5824 70 | 71 | 72 | def test_dst_transition(): 73 | start = pendulum.datetime(2017, 3, 7, tz="America/Toronto") 74 | end = start.add(days=6) 75 | interval = end - start 76 | 77 | assert interval.days == 5 78 | assert interval.seconds == 82800 79 | 80 | assert interval.remaining_days == 6 81 | assert interval.hours == 0 82 | assert interval.remaining_seconds == 0 83 | 84 | assert interval.in_days() == 6 85 | assert interval.in_hours() == 5 * 24 + 23 86 | 87 | 88 | def test_timedelta_behavior(): 89 | dt1 = pendulum.DateTime(2000, 11, 20, 1) 90 | dt2 = pendulum.DateTime(2000, 11, 25, 2) 91 | dt3 = pendulum.DateTime(2016, 11, 5, 3) 92 | 93 | p1 = pendulum.interval(dt1, dt3) 94 | p2 = pendulum.interval(dt2, dt3) 95 | it1 = p1.as_timedelta() 96 | it2 = p2.as_timedelta() 97 | 98 | assert it1.total_seconds() == p1.total_seconds() 99 | assert it2.total_seconds() == p2.total_seconds() 100 | assert it1.days == p1.days 101 | assert it2.days == p2.days 102 | assert it1.seconds == p1.seconds 103 | assert it2.seconds == p2.seconds 104 | assert it1.microseconds == p1.microseconds 105 | assert it2.microseconds == p2.microseconds 106 | 107 | 108 | def test_different_timezones_same_time(): 109 | dt1 = pendulum.datetime(2013, 3, 31, 1, 30, tz="Europe/Paris") 110 | dt2 = pendulum.datetime(2013, 4, 1, 1, 30, tz="Europe/Paris") 111 | interval = dt2 - dt1 112 | 113 | assert interval.in_words() == "1 day" 114 | assert interval.in_hours() == 23 115 | 116 | dt1 = pendulum.datetime(2013, 3, 31, 1, 30, tz="Europe/Paris") 117 | dt2 = pendulum.datetime(2013, 4, 1, 1, 30, tz="America/Toronto") 118 | interval = dt2 - dt1 119 | 120 | assert interval.in_words() == "1 day 5 hours" 121 | assert interval.in_hours() == 29 122 | -------------------------------------------------------------------------------- /tests/interval/test_hashing.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pendulum 4 | 5 | 6 | def test_intervals_with_same_duration_and_different_dates(): 7 | day1 = pendulum.DateTime(2018, 1, 1) 8 | day2 = pendulum.DateTime(2018, 1, 2) 9 | day3 = pendulum.DateTime(2018, 1, 2) 10 | 11 | interval1 = day2 - day1 12 | interval2 = day3 - day2 13 | 14 | assert interval1 != interval2 15 | assert len({interval1, interval2}) == 2 16 | 17 | 18 | def test_intervals_with_same_dates(): 19 | interval1 = pendulum.DateTime(2018, 1, 2) - pendulum.DateTime(2018, 1, 1) 20 | interval2 = pendulum.DateTime(2018, 1, 2) - pendulum.DateTime(2018, 1, 1) 21 | 22 | assert interval1 == interval2 23 | assert len({interval1, interval2}) == 1 24 | -------------------------------------------------------------------------------- /tests/interval/test_in_words.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pendulum 4 | 5 | 6 | def test_week(): 7 | start_date = pendulum.datetime(2012, 1, 1) 8 | interval = pendulum.interval(start=start_date, end=start_date.add(weeks=1)) 9 | assert interval.in_words() == "1 week" 10 | 11 | 12 | def test_week_and_day(): 13 | start_date = pendulum.datetime(2012, 1, 1) 14 | interval = pendulum.interval(start=start_date, end=start_date.add(weeks=1, days=1)) 15 | assert interval.in_words() == "1 week 1 day" 16 | 17 | 18 | def test_all(): 19 | start_date = pendulum.datetime(2012, 1, 1) 20 | interval = pendulum.interval( 21 | start=start_date, 22 | end=start_date.add(years=1, months=1, days=1, seconds=1, microseconds=1), 23 | ) 24 | assert interval.in_words() == "1 year 1 month 1 day 1 second" 25 | 26 | 27 | def test_in_french(): 28 | start_date = pendulum.datetime(2012, 1, 1) 29 | interval = pendulum.interval( 30 | start=start_date, 31 | end=start_date.add(years=1, months=1, days=1, seconds=1, microseconds=1), 32 | ) 33 | assert interval.in_words(locale="fr") == "1 an 1 mois 1 jour 1 seconde" 34 | 35 | 36 | def test_singular_negative_values(): 37 | start_date = pendulum.datetime(2012, 1, 1) 38 | interval = pendulum.interval(start=start_date, end=start_date.subtract(days=1)) 39 | assert interval.in_words() == "-1 day" 40 | 41 | 42 | def test_separator(): 43 | start_date = pendulum.datetime(2012, 1, 1) 44 | interval = pendulum.interval( 45 | start=start_date, 46 | end=start_date.add(years=1, months=1, days=1, seconds=1, microseconds=1), 47 | ) 48 | assert interval.in_words(separator=", ") == "1 year, 1 month, 1 day, 1 second" 49 | 50 | 51 | def test_subseconds(): 52 | start_date = pendulum.datetime(2012, 1, 1) 53 | interval = pendulum.interval( 54 | start=start_date, end=start_date.add(microseconds=123456) 55 | ) 56 | assert interval.in_words() == "0.12 second" 57 | 58 | 59 | def test_subseconds_with_seconds(): 60 | start_date = pendulum.datetime(2012, 1, 1) 61 | interval = pendulum.interval( 62 | start=start_date, end=start_date.add(seconds=12, microseconds=123456) 63 | ) 64 | assert interval.in_words() == "12 seconds" 65 | 66 | 67 | def test_zero_interval(): 68 | start_date = pendulum.datetime(2012, 1, 1) 69 | interval = pendulum.interval(start=start_date, end=start_date) 70 | assert interval.in_words() == "0 microseconds" 71 | -------------------------------------------------------------------------------- /tests/interval/test_range.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pendulum 4 | 5 | from pendulum.interval import Interval 6 | from tests.conftest import assert_datetime 7 | 8 | 9 | def test_range(): 10 | dt1 = pendulum.datetime(2000, 1, 1, 12, 45, 37) 11 | dt2 = pendulum.datetime(2000, 1, 31, 12, 45, 37) 12 | 13 | p = Interval(dt1, dt2) 14 | r = list(p.range("days")) 15 | 16 | assert len(r) == 31 17 | assert_datetime(r[0], 2000, 1, 1, 12, 45, 37) 18 | assert_datetime(r[-1], 2000, 1, 31, 12, 45, 37) 19 | 20 | 21 | def test_range_no_overflow(): 22 | dt1 = pendulum.datetime(2000, 1, 1, 12, 45, 37) 23 | dt2 = pendulum.datetime(2000, 1, 31, 11, 45, 37) 24 | 25 | p = Interval(dt1, dt2) 26 | r = list(p.range("days")) 27 | 28 | assert len(r) == 30 29 | assert_datetime(r[0], 2000, 1, 1, 12, 45, 37) 30 | assert_datetime(r[-1], 2000, 1, 30, 12, 45, 37) 31 | 32 | 33 | def test_range_inverted(): 34 | dt1 = pendulum.datetime(2000, 1, 1, 12, 45, 37) 35 | dt2 = pendulum.datetime(2000, 1, 31, 12, 45, 37) 36 | 37 | p = Interval(dt2, dt1) 38 | r = list(p.range("days")) 39 | 40 | assert len(r) == 31 41 | assert_datetime(r[-1], 2000, 1, 1, 12, 45, 37) 42 | assert_datetime(r[0], 2000, 1, 31, 12, 45, 37) 43 | 44 | 45 | def test_iter(): 46 | dt1 = pendulum.datetime(2000, 1, 1, 12, 45, 37) 47 | dt2 = pendulum.datetime(2000, 1, 31, 12, 45, 37) 48 | 49 | p = Interval(dt1, dt2) 50 | i = 0 51 | for dt in p: 52 | assert isinstance(dt, pendulum.DateTime) 53 | i += 1 54 | 55 | assert i == 31 56 | 57 | 58 | def test_contains(): 59 | dt1 = pendulum.datetime(2000, 1, 1, 12, 45, 37) 60 | dt2 = pendulum.datetime(2000, 1, 31, 12, 45, 37) 61 | 62 | p = pendulum.interval(dt1, dt2) 63 | dt = pendulum.datetime(2000, 1, 7) 64 | assert dt in p 65 | 66 | 67 | def test_not_contains(): 68 | dt1 = pendulum.datetime(2000, 1, 1, 12, 45, 37) 69 | dt2 = pendulum.datetime(2000, 1, 31, 12, 45, 37) 70 | 71 | p = pendulum.interval(dt1, dt2) 72 | dt = pendulum.datetime(2000, 1, 1, 11, 45, 37) 73 | assert dt not in p 74 | 75 | 76 | def test_contains_with_datetime(): 77 | dt1 = pendulum.datetime(2000, 1, 1, 12, 45, 37) 78 | dt2 = pendulum.datetime(2000, 1, 31, 12, 45, 37) 79 | 80 | p = pendulum.interval(dt1, dt2) 81 | dt = pendulum.datetime(2000, 1, 7) 82 | assert dt in p 83 | 84 | 85 | def test_range_months_overflow(): 86 | dt1 = pendulum.datetime(2016, 1, 30, tz="America/Sao_Paulo") 87 | dt2 = dt1.add(months=4) 88 | 89 | p = pendulum.interval(dt1, dt2) 90 | r = list(p.range("months")) 91 | 92 | assert_datetime(r[0], 2016, 1, 30, 0, 0, 0) 93 | assert_datetime(r[-1], 2016, 5, 30, 0, 0, 0) 94 | 95 | 96 | def test_range_with_dst(): 97 | dt1 = pendulum.datetime(2016, 10, 14, tz="America/Sao_Paulo") 98 | dt2 = dt1.add(weeks=1) 99 | 100 | p = pendulum.interval(dt1, dt2) 101 | r = list(p.range("days")) 102 | 103 | assert_datetime(r[0], 2016, 10, 14, 0, 0, 0) 104 | assert_datetime(r[2], 2016, 10, 16, 1, 0, 0) 105 | assert_datetime(r[-1], 2016, 10, 21, 0, 0, 0) 106 | 107 | 108 | def test_range_amount(): 109 | dt1 = pendulum.datetime(2016, 10, 14, tz="America/Sao_Paulo") 110 | dt2 = dt1.add(weeks=1) 111 | 112 | p = pendulum.interval(dt1, dt2) 113 | r = list(p.range("days", 2)) 114 | 115 | assert len(r) == 4 116 | assert_datetime(r[0], 2016, 10, 14, 0, 0, 0) 117 | assert_datetime(r[1], 2016, 10, 16, 1, 0, 0) 118 | assert_datetime(r[2], 2016, 10, 18, 0, 0, 0) 119 | assert_datetime(r[3], 2016, 10, 20, 0, 0, 0) 120 | -------------------------------------------------------------------------------- /tests/localization/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-pendulum/pendulum/fc386be2623f711364c599df3e208eceb4dfa23b/tests/localization/__init__.py -------------------------------------------------------------------------------- /tests/localization/test_bg.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pendulum 4 | 5 | 6 | locale = "bg" 7 | 8 | 9 | def test_diff_for_humans(): 10 | with pendulum.travel_to(pendulum.datetime(2016, 8, 29), freeze=True): 11 | diff_for_humans() 12 | 13 | 14 | def diff_for_humans(): 15 | d = pendulum.now().subtract(seconds=1) 16 | assert d.diff_for_humans(locale=locale) == "преди 1 секунда" 17 | 18 | d = pendulum.now().subtract(seconds=2) 19 | assert d.diff_for_humans(locale=locale) == "преди 2 секунди" 20 | 21 | d = pendulum.now().subtract(seconds=5) 22 | assert d.diff_for_humans(locale=locale) == "преди 5 секунди" 23 | 24 | d = pendulum.now().subtract(seconds=21) 25 | assert d.diff_for_humans(locale=locale) == "преди 21 секунди" 26 | 27 | d = pendulum.now().subtract(minutes=1) 28 | assert d.diff_for_humans(locale=locale) == "преди 1 минута" 29 | 30 | d = pendulum.now().subtract(minutes=2) 31 | assert d.diff_for_humans(locale=locale) == "преди 2 минути" 32 | 33 | d = pendulum.now().subtract(minutes=5) 34 | assert d.diff_for_humans(locale=locale) == "преди 5 минути" 35 | 36 | d = pendulum.now().subtract(hours=1) 37 | assert d.diff_for_humans(locale=locale) == "преди 1 час" 38 | 39 | d = pendulum.now().subtract(hours=2) 40 | assert d.diff_for_humans(locale=locale) == "преди 2 часа" 41 | 42 | d = pendulum.now().subtract(hours=5) 43 | assert d.diff_for_humans(locale=locale) == "преди 5 часа" 44 | 45 | d = pendulum.now().subtract(days=1) 46 | assert d.diff_for_humans(locale=locale) == "преди 1 ден" 47 | 48 | d = pendulum.now().subtract(days=2) 49 | assert d.diff_for_humans(locale=locale) == "преди 2 дни" 50 | 51 | d = pendulum.now().subtract(days=5) 52 | assert d.diff_for_humans(locale=locale) == "преди 5 дни" 53 | 54 | d = pendulum.now().subtract(weeks=1) 55 | assert d.diff_for_humans(locale=locale) == "преди 1 седмица" 56 | 57 | d = pendulum.now().subtract(weeks=2) 58 | assert d.diff_for_humans(locale=locale) == "преди 2 седмици" 59 | 60 | d = pendulum.now().subtract(months=1) 61 | assert d.diff_for_humans(locale=locale) == "преди 1 месец" 62 | 63 | d = pendulum.now().subtract(months=2) 64 | assert d.diff_for_humans(locale=locale) == "преди 2 месеца" 65 | 66 | d = pendulum.now().subtract(months=5) 67 | assert d.diff_for_humans(locale=locale) == "преди 5 месеца" 68 | 69 | d = pendulum.now().subtract(years=1) 70 | assert d.diff_for_humans(locale=locale) == "преди 1 година" 71 | 72 | d = pendulum.now().subtract(years=2) 73 | assert d.diff_for_humans(locale=locale) == "преди 2 години" 74 | 75 | d = pendulum.now().subtract(years=5) 76 | assert d.diff_for_humans(locale=locale) == "преди 5 години" 77 | 78 | d = pendulum.now().add(seconds=1) 79 | assert d.diff_for_humans(locale=locale) == "след 1 секунда" 80 | 81 | d = pendulum.now().add(seconds=1) 82 | d2 = pendulum.now() 83 | assert d.diff_for_humans(d2, locale=locale) == "след 1 секунда" 84 | assert d2.diff_for_humans(d, locale=locale) == "преди 1 секунда" 85 | 86 | assert d.diff_for_humans(d2, True, locale=locale) == "1 секунда" 87 | assert d2.diff_for_humans(d.add(seconds=1), True, locale=locale) == "2 секунди" 88 | -------------------------------------------------------------------------------- /tests/localization/test_da.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pendulum 4 | 5 | 6 | locale = "da" 7 | 8 | 9 | def test_diff_for_humans(): 10 | with pendulum.travel_to(pendulum.datetime(2016, 8, 29), freeze=True): 11 | diff_for_humans() 12 | 13 | 14 | def diff_for_humans(): 15 | d = pendulum.now().subtract(seconds=1) 16 | assert d.diff_for_humans(locale=locale) == "for 1 sekund siden" 17 | 18 | d = pendulum.now().subtract(seconds=2) 19 | assert d.diff_for_humans(locale=locale) == "for 2 sekunder siden" 20 | 21 | d = pendulum.now().subtract(minutes=1) 22 | assert d.diff_for_humans(locale=locale) == "for 1 minut siden" 23 | 24 | d = pendulum.now().subtract(minutes=2) 25 | assert d.diff_for_humans(locale=locale) == "for 2 minutter siden" 26 | 27 | d = pendulum.now().subtract(hours=1) 28 | assert d.diff_for_humans(locale=locale) == "for 1 time siden" 29 | 30 | d = pendulum.now().subtract(hours=2) 31 | assert d.diff_for_humans(locale=locale) == "for 2 timer siden" 32 | 33 | d = pendulum.now().subtract(days=1) 34 | assert d.diff_for_humans(locale=locale) == "for 1 dag siden" 35 | 36 | d = pendulum.now().subtract(days=2) 37 | assert d.diff_for_humans(locale=locale) == "for 2 dage siden" 38 | 39 | d = pendulum.now().subtract(weeks=1) 40 | assert d.diff_for_humans(locale=locale) == "for 1 uge siden" 41 | 42 | d = pendulum.now().subtract(weeks=2) 43 | assert d.diff_for_humans(locale=locale) == "for 2 uger siden" 44 | 45 | d = pendulum.now().subtract(months=1) 46 | assert d.diff_for_humans(locale=locale) == "for 1 måned siden" 47 | 48 | d = pendulum.now().subtract(months=2) 49 | assert d.diff_for_humans(locale=locale) == "for 2 måneder siden" 50 | 51 | d = pendulum.now().subtract(years=1) 52 | assert d.diff_for_humans(locale=locale) == "for 1 år siden" 53 | 54 | d = pendulum.now().subtract(years=2) 55 | assert d.diff_for_humans(locale=locale) == "for 2 år siden" 56 | 57 | d = pendulum.now().add(seconds=1) 58 | assert d.diff_for_humans(locale=locale) == "om 1 sekund" 59 | 60 | d = pendulum.now().add(seconds=1) 61 | d2 = pendulum.now() 62 | assert d.diff_for_humans(d2, locale=locale) == "1 sekund efter" 63 | assert d2.diff_for_humans(d, locale=locale) == "1 sekund før" 64 | 65 | assert d.diff_for_humans(d2, True, locale=locale) == "1 sekund" 66 | assert d2.diff_for_humans(d.add(seconds=1), True, locale=locale) == "2 sekunder" 67 | -------------------------------------------------------------------------------- /tests/localization/test_de.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pendulum 4 | 5 | 6 | locale = "de" 7 | 8 | 9 | def test_diff_for_humans(): 10 | with pendulum.travel_to(pendulum.datetime(2016, 8, 29), freeze=True): 11 | diff_for_humans() 12 | 13 | 14 | def diff_for_humans(): 15 | d = pendulum.now().subtract(seconds=1) 16 | assert d.diff_for_humans(locale=locale) == "vor 1 Sekunde" 17 | 18 | d = pendulum.now().subtract(seconds=2) 19 | assert d.diff_for_humans(locale=locale) == "vor 2 Sekunden" 20 | 21 | d = pendulum.now().subtract(minutes=1) 22 | assert d.diff_for_humans(locale=locale) == "vor 1 Minute" 23 | 24 | d = pendulum.now().subtract(minutes=2) 25 | assert d.diff_for_humans(locale=locale) == "vor 2 Minuten" 26 | 27 | d = pendulum.now().subtract(hours=1) 28 | assert d.diff_for_humans(locale=locale) == "vor 1 Stunde" 29 | 30 | d = pendulum.now().subtract(hours=2) 31 | assert d.diff_for_humans(locale=locale) == "vor 2 Stunden" 32 | 33 | d = pendulum.now().subtract(days=1) 34 | assert d.diff_for_humans(locale=locale) == "vor 1 Tag" 35 | 36 | d = pendulum.now().subtract(days=2) 37 | assert d.diff_for_humans(locale=locale) == "vor 2 Tagen" 38 | 39 | d = pendulum.now().subtract(weeks=1) 40 | assert d.diff_for_humans(locale=locale) == "vor 1 Woche" 41 | 42 | d = pendulum.now().subtract(weeks=2) 43 | assert d.diff_for_humans(locale=locale) == "vor 2 Wochen" 44 | 45 | d = pendulum.now().subtract(months=1) 46 | assert d.diff_for_humans(locale=locale) == "vor 1 Monat" 47 | 48 | d = pendulum.now().subtract(months=2) 49 | assert d.diff_for_humans(locale=locale) == "vor 2 Monaten" 50 | 51 | d = pendulum.now().subtract(years=1) 52 | assert d.diff_for_humans(locale=locale) == "vor 1 Jahr" 53 | 54 | d = pendulum.now().subtract(years=2) 55 | assert d.diff_for_humans(locale=locale) == "vor 2 Jahren" 56 | 57 | d = pendulum.now().add(seconds=1) 58 | assert d.diff_for_humans(locale=locale) == "in 1 Sekunde" 59 | 60 | d = pendulum.now().add(seconds=1) 61 | d2 = pendulum.now() 62 | assert d.diff_for_humans(d2, locale=locale) == "1 Sekunde später" 63 | assert d2.diff_for_humans(d, locale=locale) == "1 Sekunde zuvor" 64 | 65 | assert d.diff_for_humans(d2, True, locale=locale) == "1 Sekunde" 66 | assert d2.diff_for_humans(d.add(seconds=1), True, locale=locale) == "2 Sekunden" 67 | -------------------------------------------------------------------------------- /tests/localization/test_es.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pendulum 4 | 5 | 6 | locale = "es" 7 | 8 | 9 | def test_diff_for_humans(): 10 | with pendulum.travel_to(pendulum.datetime(2016, 8, 29), freeze=True): 11 | diff_for_humans() 12 | 13 | 14 | def diff_for_humans(): 15 | d = pendulum.now().subtract(seconds=1) 16 | assert d.diff_for_humans(locale=locale) == "hace unos segundos" 17 | 18 | d = pendulum.now().subtract(seconds=2) 19 | assert d.diff_for_humans(locale=locale) == "hace unos segundos" 20 | 21 | d = pendulum.now().subtract(minutes=1) 22 | assert d.diff_for_humans(locale=locale) == "hace 1 minuto" 23 | 24 | d = pendulum.now().subtract(minutes=2) 25 | assert d.diff_for_humans(locale=locale) == "hace 2 minutos" 26 | 27 | d = pendulum.now().subtract(hours=1) 28 | assert d.diff_for_humans(locale=locale) == "hace 1 hora" 29 | 30 | d = pendulum.now().subtract(hours=2) 31 | assert d.diff_for_humans(locale=locale) == "hace 2 horas" 32 | 33 | d = pendulum.now().subtract(days=1) 34 | assert d.diff_for_humans(locale=locale) == "hace 1 día" 35 | 36 | d = pendulum.now().subtract(days=2) 37 | assert d.diff_for_humans(locale=locale) == "hace 2 días" 38 | 39 | d = pendulum.now().subtract(weeks=1) 40 | assert d.diff_for_humans(locale=locale) == "hace 1 semana" 41 | 42 | d = pendulum.now().subtract(weeks=2) 43 | assert d.diff_for_humans(locale=locale) == "hace 2 semanas" 44 | 45 | d = pendulum.now().subtract(months=1) 46 | assert d.diff_for_humans(locale=locale) == "hace 1 mes" 47 | 48 | d = pendulum.now().subtract(months=2) 49 | assert d.diff_for_humans(locale=locale) == "hace 2 meses" 50 | 51 | d = pendulum.now().subtract(years=1) 52 | assert d.diff_for_humans(locale=locale) == "hace 1 año" 53 | 54 | d = pendulum.now().subtract(years=2) 55 | assert d.diff_for_humans(locale=locale) == "hace 2 años" 56 | 57 | d = pendulum.now().add(seconds=1) 58 | assert d.diff_for_humans(locale=locale) == "dentro de unos segundos" 59 | 60 | d = pendulum.now().add(seconds=1) 61 | d2 = pendulum.now() 62 | assert d.diff_for_humans(d2, locale=locale) == "unos segundos después" 63 | assert d2.diff_for_humans(d, locale=locale) == "unos segundos antes" 64 | 65 | assert d.diff_for_humans(d2, True, locale=locale) == "unos segundos" 66 | assert d2.diff_for_humans(d.add(seconds=1), True, locale=locale) == "unos segundos" 67 | -------------------------------------------------------------------------------- /tests/localization/test_fa.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pendulum 4 | 5 | 6 | locale = "fa" 7 | 8 | 9 | def test_diff_for_humans(): 10 | with pendulum.travel_to(pendulum.datetime(2016, 8, 29), freeze=True): 11 | diff_for_humans() 12 | 13 | 14 | def diff_for_humans(): 15 | d = pendulum.now().subtract(seconds=1) 16 | assert d.diff_for_humans(locale=locale) == "1 ثانیه پیش" 17 | 18 | d = pendulum.now().subtract(seconds=2) 19 | assert d.diff_for_humans(locale=locale) == "2 ثانیه پیش" 20 | 21 | d = pendulum.now().subtract(minutes=1) 22 | assert d.diff_for_humans(locale=locale) == "1 دقیقه پیش" 23 | 24 | d = pendulum.now().subtract(minutes=2) 25 | assert d.diff_for_humans(locale=locale) == "2 دقیقه پیش" 26 | 27 | d = pendulum.now().subtract(hours=1) 28 | assert d.diff_for_humans(locale=locale) == "1 ساعت پیش" 29 | 30 | d = pendulum.now().subtract(hours=2) 31 | assert d.diff_for_humans(locale=locale) == "2 ساعت پیش" 32 | 33 | d = pendulum.now().subtract(days=1) 34 | assert d.diff_for_humans(locale=locale) == "1 روز پیش" 35 | 36 | d = pendulum.now().subtract(days=2) 37 | assert d.diff_for_humans(locale=locale) == "2 روز پیش" 38 | 39 | d = pendulum.now().subtract(weeks=1) 40 | assert d.diff_for_humans(locale=locale) == "1 هفته پیش" 41 | 42 | d = pendulum.now().subtract(weeks=2) 43 | assert d.diff_for_humans(locale=locale) == "2 هفته پیش" 44 | 45 | d = pendulum.now().subtract(months=1) 46 | assert d.diff_for_humans(locale=locale) == "1 ماه پیش" 47 | 48 | d = pendulum.now().subtract(months=2) 49 | assert d.diff_for_humans(locale=locale) == "2 ماه پیش" 50 | 51 | d = pendulum.now().subtract(years=1) 52 | assert d.diff_for_humans(locale=locale) == "1 سال پیش" 53 | 54 | d = pendulum.now().subtract(years=2) 55 | assert d.diff_for_humans(locale=locale) == "2 سال پیش" 56 | 57 | d = pendulum.now().add(seconds=1) 58 | assert d.diff_for_humans(locale=locale) == "1 ثانیه بعد" 59 | 60 | d = pendulum.now().add(seconds=1) 61 | d2 = pendulum.now() 62 | assert d.diff_for_humans(d2, locale=locale) == "1 ثانیه پس از" 63 | assert d2.diff_for_humans(d, locale=locale) == "1 ثانیه پیش از" 64 | 65 | assert d.diff_for_humans(d2, True, locale=locale) == "1 ثانیه" 66 | assert d2.diff_for_humans(d.add(seconds=1), True, locale=locale) == "2 ثانیه" 67 | -------------------------------------------------------------------------------- /tests/localization/test_fo.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pendulum 4 | 5 | 6 | locale = "fo" 7 | 8 | 9 | def test_diff_for_humans(): 10 | with pendulum.travel_to(pendulum.datetime(2016, 8, 29), freeze=True): 11 | diff_for_humans() 12 | 13 | 14 | def diff_for_humans(): 15 | d = pendulum.now().subtract(seconds=1) 16 | assert d.diff_for_humans(locale=locale) == "1 sekund síðan" 17 | 18 | d = pendulum.now().subtract(seconds=2) 19 | assert d.diff_for_humans(locale=locale) == "2 sekund síðan" 20 | 21 | d = pendulum.now().subtract(minutes=1) 22 | assert d.diff_for_humans(locale=locale) == "1 minutt síðan" 23 | 24 | d = pendulum.now().subtract(minutes=2) 25 | assert d.diff_for_humans(locale=locale) == "2 minuttir síðan" 26 | 27 | d = pendulum.now().subtract(hours=1) 28 | assert d.diff_for_humans(locale=locale) == "1 tími síðan" 29 | 30 | d = pendulum.now().subtract(hours=2) 31 | assert d.diff_for_humans(locale=locale) == "2 tímar síðan" 32 | 33 | d = pendulum.now().subtract(days=1) 34 | assert d.diff_for_humans(locale=locale) == "1 dagur síðan" 35 | 36 | d = pendulum.now().subtract(days=2) 37 | assert d.diff_for_humans(locale=locale) == "2 dagar síðan" 38 | 39 | d = pendulum.now().subtract(weeks=1) 40 | assert d.diff_for_humans(locale=locale) == "1 vika síðan" 41 | 42 | d = pendulum.now().subtract(weeks=2) 43 | assert d.diff_for_humans(locale=locale) == "2 vikur síðan" 44 | 45 | d = pendulum.now().subtract(months=1) 46 | assert d.diff_for_humans(locale=locale) == "1 mánað síðan" 47 | 48 | d = pendulum.now().subtract(months=2) 49 | assert d.diff_for_humans(locale=locale) == "2 mánaðir síðan" 50 | 51 | d = pendulum.now().subtract(years=1) 52 | assert d.diff_for_humans(locale=locale) == "1 ár síðan" 53 | 54 | d = pendulum.now().subtract(years=2) 55 | assert d.diff_for_humans(locale=locale) == "2 ár síðan" 56 | 57 | d = pendulum.now().add(seconds=1) 58 | assert d.diff_for_humans(locale=locale) == "um 1 sekund" 59 | 60 | d = pendulum.now().add(seconds=1) 61 | d2 = pendulum.now() 62 | assert d.diff_for_humans(d2, locale=locale) == "1 sekund aftaná" 63 | assert d2.diff_for_humans(d, locale=locale) == "1 sekund áðrenn" 64 | 65 | assert d.diff_for_humans(d2, True, locale=locale) == "1 sekund" 66 | assert d2.diff_for_humans(d.add(seconds=1), True, locale=locale) == "2 sekundir" 67 | -------------------------------------------------------------------------------- /tests/localization/test_fr.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pendulum 4 | 5 | 6 | locale = "fr" 7 | 8 | 9 | def test_diff_for_humans(): 10 | with pendulum.travel_to(pendulum.datetime(2016, 8, 29), freeze=True): 11 | diff_for_humans() 12 | 13 | 14 | def diff_for_humans(): 15 | d = pendulum.now().subtract(seconds=1) 16 | assert d.diff_for_humans(locale=locale) == "il y a quelques secondes" 17 | 18 | d = pendulum.now().subtract(seconds=2) 19 | assert d.diff_for_humans(locale=locale) == "il y a quelques secondes" 20 | 21 | d = pendulum.now().subtract(minutes=1) 22 | assert d.diff_for_humans(locale=locale) == "il y a 1 minute" 23 | 24 | d = pendulum.now().subtract(minutes=2) 25 | assert d.diff_for_humans(locale=locale) == "il y a 2 minutes" 26 | 27 | d = pendulum.now().subtract(hours=1) 28 | assert d.diff_for_humans(locale=locale) == "il y a 1 heure" 29 | 30 | d = pendulum.now().subtract(hours=2) 31 | assert d.diff_for_humans(locale=locale) == "il y a 2 heures" 32 | 33 | d = pendulum.now().subtract(days=1) 34 | assert d.diff_for_humans(locale=locale) == "il y a 1 jour" 35 | 36 | d = pendulum.now().subtract(days=2) 37 | assert d.diff_for_humans(locale=locale) == "il y a 2 jours" 38 | 39 | d = pendulum.now().subtract(weeks=1) 40 | assert d.diff_for_humans(locale=locale) == "il y a 1 semaine" 41 | 42 | d = pendulum.now().subtract(weeks=2) 43 | assert d.diff_for_humans(locale=locale) == "il y a 2 semaines" 44 | 45 | d = pendulum.now().subtract(months=1) 46 | assert d.diff_for_humans(locale=locale) == "il y a 1 mois" 47 | 48 | d = pendulum.now().subtract(months=2) 49 | assert d.diff_for_humans(locale=locale) == "il y a 2 mois" 50 | 51 | d = pendulum.now().subtract(years=1) 52 | assert d.diff_for_humans(locale=locale) == "il y a 1 an" 53 | 54 | d = pendulum.now().subtract(years=2) 55 | assert d.diff_for_humans(locale=locale) == "il y a 2 ans" 56 | 57 | d = pendulum.now().add(seconds=1) 58 | assert d.diff_for_humans(locale=locale) == "dans quelques secondes" 59 | 60 | d = pendulum.now().add(seconds=1) 61 | d2 = pendulum.now() 62 | assert d.diff_for_humans(d2, locale=locale) == "quelques secondes après" 63 | assert d2.diff_for_humans(d, locale=locale) == "quelques secondes avant" 64 | 65 | assert d.diff_for_humans(d2, True, locale=locale) == "quelques secondes" 66 | assert ( 67 | d2.diff_for_humans(d.add(seconds=1), True, locale=locale) == "quelques secondes" 68 | ) 69 | 70 | 71 | def test_format(): 72 | d = pendulum.datetime(2016, 8, 28, 7, 3, 6, 123456) 73 | assert d.format("dddd", locale=locale) == "dimanche" 74 | assert d.format("ddd", locale=locale) == "dim." 75 | assert d.format("MMMM", locale=locale) == "août" 76 | assert d.format("MMM", locale=locale) == "août" 77 | assert d.format("A", locale=locale) == "AM" 78 | assert d.format("Do", locale=locale) == "28e" 79 | 80 | assert d.format("LT", locale=locale) == "07:03" 81 | assert d.format("LTS", locale=locale) == "07:03:06" 82 | assert d.format("L", locale=locale) == "28/08/2016" 83 | assert d.format("LL", locale=locale) == "28 août 2016" 84 | assert d.format("LLL", locale=locale) == "28 août 2016 07:03" 85 | assert d.format("LLLL", locale=locale) == "dimanche 28 août 2016 07:03" 86 | -------------------------------------------------------------------------------- /tests/localization/test_he.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pendulum 4 | 5 | 6 | locale = "he" 7 | 8 | 9 | def test_diff_for_humans(): 10 | with pendulum.travel_to(pendulum.datetime(2016, 8, 29), freeze=True): 11 | diff_for_humans() 12 | 13 | 14 | def diff_for_humans(): 15 | d = pendulum.now().subtract(seconds=1) 16 | assert d.diff_for_humans(locale=locale) == "לפני כמה שניות" 17 | 18 | d = pendulum.now().subtract(seconds=2) 19 | assert d.diff_for_humans(locale=locale) == "לפני כמה שניות" 20 | 21 | d = pendulum.now().subtract(minutes=1) 22 | assert d.diff_for_humans(locale=locale) == "לפני דקה" 23 | 24 | d = pendulum.now().subtract(minutes=2) 25 | assert d.diff_for_humans(locale=locale) == "לפני שתי דקות" 26 | 27 | d = pendulum.now().subtract(hours=1) 28 | assert d.diff_for_humans(locale=locale) == "לפני שעה" 29 | 30 | d = pendulum.now().subtract(hours=2) 31 | assert d.diff_for_humans(locale=locale) == "לפני שעתיים" 32 | 33 | d = pendulum.now().subtract(days=1) 34 | assert d.diff_for_humans(locale=locale) == "לפני יום 1" 35 | 36 | d = pendulum.now().subtract(days=2) 37 | assert d.diff_for_humans(locale=locale) == "לפני יומיים" 38 | 39 | d = pendulum.now().subtract(weeks=1) 40 | assert d.diff_for_humans(locale=locale) == "לפני שבוע" 41 | 42 | d = pendulum.now().subtract(weeks=2) 43 | assert d.diff_for_humans(locale=locale) == "לפני שבועיים" 44 | 45 | d = pendulum.now().subtract(months=1) 46 | assert d.diff_for_humans(locale=locale) == "לפני חודש" 47 | 48 | d = pendulum.now().subtract(months=2) 49 | assert d.diff_for_humans(locale=locale) == "לפני חודשיים" 50 | 51 | d = pendulum.now().subtract(years=1) 52 | assert d.diff_for_humans(locale=locale) == "לפני שנה" 53 | 54 | d = pendulum.now().subtract(years=2) 55 | assert d.diff_for_humans(locale=locale) == "לפני שנתיים" 56 | 57 | d = pendulum.now().add(seconds=1) 58 | assert d.diff_for_humans(locale=locale) == "תוך כמה שניות" 59 | 60 | d = pendulum.now().add(seconds=1) 61 | d2 = pendulum.now() 62 | assert d.diff_for_humans(d2, locale=locale) == "בעוד כמה שניות" 63 | assert d2.diff_for_humans(d, locale=locale) == "כמה שניות קודם" 64 | 65 | assert d.diff_for_humans(d2, True, locale=locale) == "כמה שניות" 66 | assert d2.diff_for_humans(d.add(seconds=1), True, locale=locale) == "כמה שניות" 67 | -------------------------------------------------------------------------------- /tests/localization/test_id.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pendulum 4 | 5 | 6 | locale = "id" 7 | 8 | 9 | def test_diff_for_humans(): 10 | with pendulum.travel_to(pendulum.datetime(2016, 8, 29), freeze=True): 11 | diff_for_humans() 12 | 13 | 14 | def diff_for_humans(): 15 | d = pendulum.now().subtract(seconds=1) 16 | assert d.diff_for_humans(locale=locale) == "beberapa detik yang lalu" 17 | 18 | d = pendulum.now().subtract(seconds=2) 19 | assert d.diff_for_humans(locale=locale) == "beberapa detik yang lalu" 20 | 21 | d = pendulum.now().subtract(seconds=21) 22 | assert d.diff_for_humans(locale=locale) == "21 detik yang lalu" 23 | 24 | d = pendulum.now().subtract(minutes=1) 25 | assert d.diff_for_humans(locale=locale) == "1 menit yang lalu" 26 | 27 | d = pendulum.now().subtract(minutes=2) 28 | assert d.diff_for_humans(locale=locale) == "2 menit yang lalu" 29 | 30 | d = pendulum.now().subtract(hours=1) 31 | assert d.diff_for_humans(locale=locale) == "1 jam yang lalu" 32 | 33 | d = pendulum.now().subtract(hours=2) 34 | assert d.diff_for_humans(locale=locale) == "2 jam yang lalu" 35 | 36 | d = pendulum.now().subtract(days=1) 37 | assert d.diff_for_humans(locale=locale) == "1 hari yang lalu" 38 | 39 | d = pendulum.now().subtract(days=2) 40 | assert d.diff_for_humans(locale=locale) == "2 hari yang lalu" 41 | 42 | d = pendulum.now().subtract(weeks=1) 43 | assert d.diff_for_humans(locale=locale) == "1 minggu yang lalu" 44 | 45 | d = pendulum.now().subtract(weeks=2) 46 | assert d.diff_for_humans(locale=locale) == "2 minggu yang lalu" 47 | 48 | d = pendulum.now().subtract(months=1) 49 | assert d.diff_for_humans(locale=locale) == "1 bulan yang lalu" 50 | 51 | d = pendulum.now().subtract(months=2) 52 | assert d.diff_for_humans(locale=locale) == "2 bulan yang lalu" 53 | 54 | d = pendulum.now().subtract(years=1) 55 | assert d.diff_for_humans(locale=locale) == "1 tahun yang lalu" 56 | 57 | d = pendulum.now().subtract(years=2) 58 | assert d.diff_for_humans(locale=locale) == "2 tahun yang lalu" 59 | 60 | d = pendulum.now().add(seconds=1) 61 | assert d.diff_for_humans(locale=locale) == "dalam beberapa detik" 62 | 63 | d = pendulum.now().add(seconds=1) 64 | d2 = pendulum.now() 65 | assert d.diff_for_humans(d2, locale=locale) == "beberapa detik kemudian" 66 | assert d2.diff_for_humans(d, locale=locale) == "beberapa detik yang lalu" 67 | 68 | assert d.diff_for_humans(d2, True, locale=locale) == "beberapa detik" 69 | assert d2.diff_for_humans(d.add(seconds=1), True, locale=locale) == "beberapa detik" 70 | -------------------------------------------------------------------------------- /tests/localization/test_it.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pendulum 4 | 5 | 6 | locale = "it" 7 | 8 | 9 | def test_diff_for_humans(): 10 | with pendulum.travel_to(pendulum.datetime(2016, 8, 29), freeze=True): 11 | diff_for_humans() 12 | 13 | 14 | def diff_for_humans(): 15 | d = pendulum.now().subtract(seconds=1) 16 | assert d.diff_for_humans(locale=locale) == "alcuni secondi fa" 17 | 18 | d = pendulum.now().subtract(seconds=2) 19 | assert d.diff_for_humans(locale=locale) == "alcuni secondi fa" 20 | 21 | d = pendulum.now().subtract(minutes=1) 22 | assert d.diff_for_humans(locale=locale) == "1 minuto fa" 23 | 24 | d = pendulum.now().subtract(minutes=2) 25 | assert d.diff_for_humans(locale=locale) == "2 minuti fa" 26 | 27 | d = pendulum.now().subtract(hours=1) 28 | assert d.diff_for_humans(locale=locale) == "1 ora fa" 29 | 30 | d = pendulum.now().subtract(hours=2) 31 | assert d.diff_for_humans(locale=locale) == "2 ore fa" 32 | 33 | d = pendulum.now().subtract(days=1) 34 | assert d.diff_for_humans(locale=locale) == "1 giorno fa" 35 | 36 | d = pendulum.now().subtract(days=2) 37 | assert d.diff_for_humans(locale=locale) == "2 giorni fa" 38 | 39 | d = pendulum.now().subtract(weeks=1) 40 | assert d.diff_for_humans(locale=locale) == "1 settimana fa" 41 | 42 | d = pendulum.now().subtract(weeks=2) 43 | assert d.diff_for_humans(locale=locale) == "2 settimane fa" 44 | 45 | d = pendulum.now().subtract(months=1) 46 | assert d.diff_for_humans(locale=locale) == "1 mese fa" 47 | 48 | d = pendulum.now().subtract(months=2) 49 | assert d.diff_for_humans(locale=locale) == "2 mesi fa" 50 | 51 | d = pendulum.now().subtract(years=1) 52 | assert d.diff_for_humans(locale=locale) == "1 anno fa" 53 | 54 | d = pendulum.now().subtract(years=2) 55 | assert d.diff_for_humans(locale=locale) == "2 anni fa" 56 | 57 | d = pendulum.now().add(seconds=1) 58 | assert d.diff_for_humans(locale=locale) == "in alcuni secondi" 59 | 60 | d = pendulum.now().add(seconds=1) 61 | d2 = pendulum.now() 62 | assert d.diff_for_humans(d2, locale=locale) == "alcuni secondi dopo" 63 | assert d2.diff_for_humans(d, locale=locale) == "alcuni secondi prima" 64 | 65 | assert d.diff_for_humans(d2, True, locale=locale) == "alcuni secondi" 66 | assert d2.diff_for_humans(d.add(seconds=1), True, locale=locale) == "alcuni secondi" 67 | 68 | 69 | def test_format(): 70 | d = pendulum.datetime(2016, 8, 28, 7, 3, 6, 123456) 71 | assert d.format("dddd", locale=locale) == "domenica" 72 | assert d.format("ddd", locale=locale) == "dom" 73 | assert d.format("MMMM", locale=locale) == "agosto" 74 | assert d.format("MMM", locale=locale) == "ago" 75 | assert d.format("A", locale=locale) == "AM" 76 | 77 | assert d.format("LT", locale=locale) == "7:03" 78 | assert d.format("LTS", locale=locale) == "7:03:06" 79 | assert d.format("L", locale=locale) == "28/08/2016" 80 | assert d.format("LL", locale=locale) == "28 agosto 2016" 81 | assert d.format("LLL", locale=locale) == "28 agosto 2016 alle 7:03" 82 | assert d.format("LLLL", locale=locale) == "domenica, 28 agosto 2016 alle 7:03" 83 | 84 | assert d.format("Do", locale=locale) == "28°" 85 | d = pendulum.datetime(2019, 1, 1, 7, 3, 6, 123456) 86 | assert d.format("Do", locale=locale) == "1°" 87 | -------------------------------------------------------------------------------- /tests/localization/test_ja.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pendulum 4 | 5 | 6 | locale = "ja" 7 | 8 | 9 | def test_diff_for_humans(): 10 | with pendulum.travel_to(pendulum.datetime(2016, 8, 29), freeze=True): 11 | diff_for_humans() 12 | 13 | 14 | def diff_for_humans(): 15 | d = pendulum.now().subtract(seconds=1) 16 | assert d.diff_for_humans(locale=locale) == "数秒 前に" 17 | 18 | d = pendulum.now().subtract(seconds=2) 19 | assert d.diff_for_humans(locale=locale) == "数秒 前に" 20 | 21 | d = pendulum.now().subtract(seconds=21) 22 | assert d.diff_for_humans(locale=locale) == "21 秒前" 23 | 24 | d = pendulum.now().subtract(minutes=1) 25 | assert d.diff_for_humans(locale=locale) == "1 分前" 26 | 27 | d = pendulum.now().subtract(minutes=2) 28 | assert d.diff_for_humans(locale=locale) == "2 分前" 29 | 30 | d = pendulum.now().subtract(hours=1) 31 | assert d.diff_for_humans(locale=locale) == "1 時間前" 32 | 33 | d = pendulum.now().subtract(hours=2) 34 | assert d.diff_for_humans(locale=locale) == "2 時間前" 35 | 36 | d = pendulum.now().subtract(days=1) 37 | assert d.diff_for_humans(locale=locale) == "1 日前" 38 | 39 | d = pendulum.now().subtract(days=2) 40 | assert d.diff_for_humans(locale=locale) == "2 日前" 41 | 42 | d = pendulum.now().subtract(weeks=1) 43 | assert d.diff_for_humans(locale=locale) == "1 週間前" 44 | 45 | d = pendulum.now().subtract(weeks=2) 46 | assert d.diff_for_humans(locale=locale) == "2 週間前" 47 | 48 | d = pendulum.now().subtract(months=1) 49 | assert d.diff_for_humans(locale=locale) == "1 か月前" 50 | 51 | d = pendulum.now().subtract(months=2) 52 | assert d.diff_for_humans(locale=locale) == "2 か月前" 53 | 54 | d = pendulum.now().subtract(years=1) 55 | assert d.diff_for_humans(locale=locale) == "1 年前" 56 | 57 | d = pendulum.now().subtract(years=2) 58 | assert d.diff_for_humans(locale=locale) == "2 年前" 59 | 60 | d = pendulum.now().add(seconds=1) 61 | assert d.diff_for_humans(locale=locale) == "今から 数秒" 62 | 63 | d = pendulum.now().add(seconds=1) 64 | d2 = pendulum.now() 65 | assert d.diff_for_humans(d2, locale=locale) == "数秒 後" 66 | assert d2.diff_for_humans(d, locale=locale) == "数秒 前" 67 | 68 | assert d.diff_for_humans(d2, True, locale=locale) == "数秒" 69 | assert d2.diff_for_humans(d.add(seconds=1), True, locale=locale) == "数秒" 70 | -------------------------------------------------------------------------------- /tests/localization/test_ko.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pendulum 4 | 5 | 6 | locale = "ko" 7 | 8 | 9 | def test_diff_for_humans(): 10 | with pendulum.travel_to(pendulum.datetime(2016, 8, 29), freeze=True): 11 | diff_for_humans() 12 | 13 | 14 | def diff_for_humans(): 15 | d = pendulum.now().subtract(seconds=1) 16 | assert d.diff_for_humans(locale=locale) == "1초 전" 17 | 18 | d = pendulum.now().subtract(seconds=2) 19 | assert d.diff_for_humans(locale=locale) == "2초 전" 20 | 21 | d = pendulum.now().subtract(minutes=1) 22 | assert d.diff_for_humans(locale=locale) == "1분 전" 23 | 24 | d = pendulum.now().subtract(minutes=2) 25 | assert d.diff_for_humans(locale=locale) == "2분 전" 26 | 27 | d = pendulum.now().subtract(hours=1) 28 | assert d.diff_for_humans(locale=locale) == "1시간 전" 29 | 30 | d = pendulum.now().subtract(hours=2) 31 | assert d.diff_for_humans(locale=locale) == "2시간 전" 32 | 33 | d = pendulum.now().subtract(days=1) 34 | assert d.diff_for_humans(locale=locale) == "1일 전" 35 | 36 | d = pendulum.now().subtract(days=2) 37 | assert d.diff_for_humans(locale=locale) == "2일 전" 38 | 39 | d = pendulum.now().subtract(weeks=1) 40 | assert d.diff_for_humans(locale=locale) == "1주 전" 41 | 42 | d = pendulum.now().subtract(weeks=2) 43 | assert d.diff_for_humans(locale=locale) == "2주 전" 44 | 45 | d = pendulum.now().subtract(months=1) 46 | assert d.diff_for_humans(locale=locale) == "1개월 전" 47 | 48 | d = pendulum.now().subtract(months=2) 49 | assert d.diff_for_humans(locale=locale) == "2개월 전" 50 | 51 | d = pendulum.now().subtract(years=1) 52 | assert d.diff_for_humans(locale=locale) == "1년 전" 53 | 54 | d = pendulum.now().subtract(years=2) 55 | assert d.diff_for_humans(locale=locale) == "2년 전" 56 | 57 | d = pendulum.now().add(seconds=1) 58 | assert d.diff_for_humans(locale=locale) == "1초 후" 59 | 60 | d = pendulum.now().add(seconds=1) 61 | d2 = pendulum.now() 62 | assert d.diff_for_humans(d2, locale=locale) == "1초 후" 63 | assert d2.diff_for_humans(d, locale=locale) == "1초 전" 64 | 65 | assert d.diff_for_humans(d2, True, locale=locale) == "1초" 66 | assert d2.diff_for_humans(d.add(seconds=1), True, locale=locale) == "2초" 67 | -------------------------------------------------------------------------------- /tests/localization/test_lt.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pendulum 4 | 5 | 6 | locale = "lt" 7 | 8 | 9 | def test_diff_for_humans(): 10 | with pendulum.travel_to(pendulum.datetime(2016, 8, 29), freeze=True): 11 | diff_for_humans() 12 | 13 | 14 | def diff_for_humans(): 15 | d = pendulum.now().subtract(seconds=1) 16 | assert d.diff_for_humans(locale=locale) == "prieš 1 sekundę" 17 | 18 | d = pendulum.now().subtract(seconds=2) 19 | assert d.diff_for_humans(locale=locale) == "prieš 2 sekundes" 20 | 21 | d = pendulum.now().subtract(seconds=21) 22 | assert d.diff_for_humans(locale=locale) == "prieš 21 sekundę" 23 | 24 | d = pendulum.now().subtract(minutes=1) 25 | assert d.diff_for_humans(locale=locale) == "prieš 1 minutę" 26 | 27 | d = pendulum.now().subtract(minutes=2) 28 | assert d.diff_for_humans(locale=locale) == "prieš 2 minutes" 29 | 30 | d = pendulum.now().subtract(hours=1) 31 | assert d.diff_for_humans(locale=locale) == "prieš 1 valandą" 32 | 33 | d = pendulum.now().subtract(hours=2) 34 | assert d.diff_for_humans(locale=locale) == "prieš 2 valandas" 35 | 36 | d = pendulum.now().subtract(days=1) 37 | assert d.diff_for_humans(locale=locale) == "prieš 1 dieną" 38 | 39 | d = pendulum.now().subtract(days=2) 40 | assert d.diff_for_humans(locale=locale) == "prieš 2 dienas" 41 | 42 | d = pendulum.now().subtract(weeks=1) 43 | assert d.diff_for_humans(locale=locale) == "prieš 1 savaitę" 44 | 45 | d = pendulum.now().subtract(weeks=2) 46 | assert d.diff_for_humans(locale=locale) == "prieš 2 savaites" 47 | 48 | d = pendulum.now().subtract(months=1) 49 | assert d.diff_for_humans(locale=locale) == "prieš 1 mėnesį" 50 | 51 | d = pendulum.now().subtract(months=2) 52 | assert d.diff_for_humans(locale=locale) == "prieš 2 mėnesius" 53 | 54 | d = pendulum.now().subtract(years=1) 55 | assert d.diff_for_humans(locale=locale) == "prieš 1 metus" 56 | 57 | d = pendulum.now().subtract(years=2) 58 | assert d.diff_for_humans(locale=locale) == "prieš 2 metus" 59 | 60 | d = pendulum.now().add(seconds=1) 61 | assert d.diff_for_humans(locale=locale) == "po 1 sekundės" 62 | 63 | d = pendulum.now().add(seconds=1) 64 | d2 = pendulum.now() 65 | assert d.diff_for_humans(d2, locale=locale) == "po 1 sekundės" 66 | assert d2.diff_for_humans(d, locale=locale) == "1 sekundę nuo dabar" 67 | 68 | assert d.diff_for_humans(d2, True, locale=locale) == "1 sekundė" 69 | assert d2.diff_for_humans(d.add(seconds=1), True, locale=locale) == "2 sekundės" 70 | -------------------------------------------------------------------------------- /tests/localization/test_nb.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pendulum 4 | 5 | 6 | locale = "nb" 7 | 8 | 9 | def test_diff_for_humans(): 10 | with pendulum.travel_to(pendulum.datetime(2016, 8, 29), freeze=True): 11 | diff_for_humans() 12 | 13 | 14 | def diff_for_humans(): 15 | d = pendulum.now().subtract(seconds=1) 16 | assert d.diff_for_humans(locale=locale) == "for 1 sekund siden" 17 | 18 | d = pendulum.now().subtract(seconds=2) 19 | assert d.diff_for_humans(locale=locale) == "for 2 sekunder siden" 20 | 21 | d = pendulum.now().subtract(minutes=1) 22 | assert d.diff_for_humans(locale=locale) == "for 1 minutt siden" 23 | 24 | d = pendulum.now().subtract(minutes=2) 25 | assert d.diff_for_humans(locale=locale) == "for 2 minutter siden" 26 | 27 | d = pendulum.now().subtract(hours=1) 28 | assert d.diff_for_humans(locale=locale) == "for 1 time siden" 29 | 30 | d = pendulum.now().subtract(hours=2) 31 | assert d.diff_for_humans(locale=locale) == "for 2 timer siden" 32 | 33 | d = pendulum.now().subtract(days=1) 34 | assert d.diff_for_humans(locale=locale) == "for 1 dag siden" 35 | 36 | d = pendulum.now().subtract(days=2) 37 | assert d.diff_for_humans(locale=locale) == "for 2 dager siden" 38 | 39 | d = pendulum.now().subtract(weeks=1) 40 | assert d.diff_for_humans(locale=locale) == "for 1 uke siden" 41 | 42 | d = pendulum.now().subtract(weeks=2) 43 | assert d.diff_for_humans(locale=locale) == "for 2 uker siden" 44 | 45 | d = pendulum.now().subtract(months=1) 46 | assert d.diff_for_humans(locale=locale) == "for 1 måned siden" 47 | 48 | d = pendulum.now().subtract(months=2) 49 | assert d.diff_for_humans(locale=locale) == "for 2 måneder siden" 50 | 51 | d = pendulum.now().subtract(years=1) 52 | assert d.diff_for_humans(locale=locale) == "for 1 år siden" 53 | 54 | d = pendulum.now().subtract(years=2) 55 | assert d.diff_for_humans(locale=locale) == "for 2 år siden" 56 | 57 | d = pendulum.now().add(seconds=1) 58 | assert d.diff_for_humans(locale=locale) == "om 1 sekund" 59 | 60 | d = pendulum.now().add(seconds=1) 61 | d2 = pendulum.now() 62 | assert d.diff_for_humans(d2, locale=locale) == "1 sekund etter" 63 | assert d2.diff_for_humans(d, locale=locale) == "1 sekund før" 64 | 65 | assert d.diff_for_humans(d2, True, locale=locale) == "1 sekund" 66 | assert d2.diff_for_humans(d.add(seconds=1), True, locale=locale) == "2 sekunder" 67 | 68 | 69 | def test_format(): 70 | d = pendulum.datetime(2016, 8, 28, 7, 3, 6, 123456) 71 | assert d.format("dddd", locale=locale) == "søndag" 72 | assert d.format("ddd", locale=locale) == "søn." 73 | assert d.format("MMMM", locale=locale) == "august" 74 | assert d.format("MMM", locale=locale) == "aug." 75 | assert d.format("A", locale=locale) == "a.m." 76 | assert d.format("Qo", locale=locale) == "3." 77 | assert d.format("Mo", locale=locale) == "8." 78 | assert d.format("Do", locale=locale) == "28." 79 | 80 | assert d.format("LT", locale=locale) == "07:03" 81 | assert d.format("LTS", locale=locale) == "07:03:06" 82 | assert d.format("L", locale=locale) == "28.08.2016" 83 | assert d.format("LL", locale=locale) == "28. august 2016" 84 | assert d.format("LLL", locale=locale) == "28. august 2016 07:03" 85 | assert d.format("LLLL", locale=locale) == "søndag 28. august 2016 07:03" 86 | -------------------------------------------------------------------------------- /tests/localization/test_nl.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pendulum 4 | 5 | 6 | locale = "nl" 7 | 8 | 9 | def test_diff_for_humans(): 10 | with pendulum.travel_to(pendulum.datetime(2016, 8, 29), freeze=True): 11 | diff_for_humans() 12 | 13 | 14 | def diff_for_humans(): 15 | d = pendulum.now().subtract(seconds=1) 16 | assert d.diff_for_humans(locale=locale) == "enkele seconden geleden" 17 | 18 | d = pendulum.now().subtract(seconds=2) 19 | assert d.diff_for_humans(locale=locale) == "enkele seconden geleden" 20 | 21 | d = pendulum.now().subtract(seconds=22) 22 | assert d.diff_for_humans(locale=locale) == "22 seconden geleden" 23 | 24 | d = pendulum.now().subtract(minutes=1) 25 | assert d.diff_for_humans(locale=locale) == "1 minuut geleden" 26 | 27 | d = pendulum.now().subtract(minutes=2) 28 | assert d.diff_for_humans(locale=locale) == "2 minuten geleden" 29 | 30 | d = pendulum.now().subtract(hours=1) 31 | assert d.diff_for_humans(locale=locale) == "1 uur geleden" 32 | 33 | d = pendulum.now().subtract(hours=2) 34 | assert d.diff_for_humans(locale=locale) == "2 uur geleden" 35 | 36 | d = pendulum.now().subtract(days=1) 37 | assert d.diff_for_humans(locale=locale) == "1 dag geleden" 38 | 39 | d = pendulum.now().subtract(days=2) 40 | assert d.diff_for_humans(locale=locale) == "2 dagen geleden" 41 | 42 | d = pendulum.now().subtract(weeks=1) 43 | assert d.diff_for_humans(locale=locale) == "1 week geleden" 44 | 45 | d = pendulum.now().subtract(weeks=2) 46 | assert d.diff_for_humans(locale=locale) == "2 weken geleden" 47 | 48 | d = pendulum.now().subtract(months=1) 49 | assert d.diff_for_humans(locale=locale) == "1 maand geleden" 50 | 51 | d = pendulum.now().subtract(months=2) 52 | assert d.diff_for_humans(locale=locale) == "2 maanden geleden" 53 | 54 | d = pendulum.now().subtract(years=1) 55 | assert d.diff_for_humans(locale=locale) == "1 jaar geleden" 56 | 57 | d = pendulum.now().subtract(years=2) 58 | assert d.diff_for_humans(locale=locale) == "2 jaar geleden" 59 | 60 | d = pendulum.now().add(seconds=1) 61 | assert d.diff_for_humans(locale=locale) == "over enkele seconden" 62 | 63 | d = pendulum.now().add(weeks=1) 64 | assert d.diff_for_humans(locale=locale) == "over 1 week" 65 | 66 | d = pendulum.now().add(seconds=1) 67 | d2 = pendulum.now() 68 | assert d.diff_for_humans(d2, locale=locale) == "enkele seconden later" 69 | assert d2.diff_for_humans(d, locale=locale) == "enkele seconden eerder" 70 | 71 | assert d.diff_for_humans(d2, True, locale=locale) == "enkele seconden" 72 | assert ( 73 | d2.diff_for_humans(d.add(seconds=1), True, locale=locale) == "enkele seconden" 74 | ) 75 | 76 | 77 | def test_format(): 78 | d = pendulum.datetime(2016, 8, 28, 7, 3, 6, 123456) 79 | assert d.format("dddd", locale=locale) == "zondag" 80 | assert d.format("ddd", locale=locale) == "zo" 81 | assert d.format("MMMM", locale=locale) == "augustus" 82 | assert d.format("MMM", locale=locale) == "aug." 83 | assert d.format("A", locale=locale) == "a.m." 84 | assert d.format("Do", locale=locale) == "28e" 85 | -------------------------------------------------------------------------------- /tests/localization/test_nn.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pendulum 4 | 5 | 6 | locale = "nn" 7 | 8 | 9 | def test_diff_for_humans(): 10 | with pendulum.travel_to(pendulum.datetime(2016, 8, 29), freeze=True): 11 | diff_for_humans() 12 | 13 | 14 | def diff_for_humans(): 15 | d = pendulum.now().subtract(seconds=1) 16 | assert d.diff_for_humans(locale=locale) == "for 1 sekund sidan" 17 | 18 | d = pendulum.now().subtract(seconds=2) 19 | assert d.diff_for_humans(locale=locale) == "for 2 sekund sidan" 20 | 21 | d = pendulum.now().subtract(minutes=1) 22 | assert d.diff_for_humans(locale=locale) == "for 1 minutt sidan" 23 | 24 | d = pendulum.now().subtract(minutes=2) 25 | assert d.diff_for_humans(locale=locale) == "for 2 minutt sidan" 26 | 27 | d = pendulum.now().subtract(hours=1) 28 | assert d.diff_for_humans(locale=locale) == "for 1 time sidan" 29 | 30 | d = pendulum.now().subtract(hours=2) 31 | assert d.diff_for_humans(locale=locale) == "for 2 timar sidan" 32 | 33 | d = pendulum.now().subtract(days=1) 34 | assert d.diff_for_humans(locale=locale) == "for 1 dag sidan" 35 | 36 | d = pendulum.now().subtract(days=2) 37 | assert d.diff_for_humans(locale=locale) == "for 2 dagar sidan" 38 | 39 | d = pendulum.now().subtract(weeks=1) 40 | assert d.diff_for_humans(locale=locale) == "for 1 veke sidan" 41 | 42 | d = pendulum.now().subtract(weeks=2) 43 | assert d.diff_for_humans(locale=locale) == "for 2 veker sidan" 44 | 45 | d = pendulum.now().subtract(months=1) 46 | assert d.diff_for_humans(locale=locale) == "for 1 månad sidan" 47 | 48 | d = pendulum.now().subtract(months=2) 49 | assert d.diff_for_humans(locale=locale) == "for 2 månadar sidan" 50 | 51 | d = pendulum.now().subtract(years=1) 52 | assert d.diff_for_humans(locale=locale) == "for 1 år sidan" 53 | 54 | d = pendulum.now().subtract(years=2) 55 | assert d.diff_for_humans(locale=locale) == "for 2 år sidan" 56 | 57 | d = pendulum.now().add(seconds=1) 58 | assert d.diff_for_humans(locale=locale) == "om 1 sekund" 59 | 60 | d = pendulum.now().add(seconds=1) 61 | d2 = pendulum.now() 62 | assert d.diff_for_humans(d2, locale=locale) == "1 sekund etter" 63 | assert d2.diff_for_humans(d, locale=locale) == "1 sekund før" 64 | 65 | assert d.diff_for_humans(d2, True, locale=locale) == "1 sekund" 66 | assert d2.diff_for_humans(d.add(seconds=1), True, locale=locale) == "2 sekund" 67 | 68 | 69 | def test_format(): 70 | d = pendulum.datetime(2016, 8, 29, 7, 3, 6, 123456) 71 | assert d.format("dddd", locale=locale) == "måndag" 72 | assert d.format("ddd", locale=locale) == "mån." 73 | assert d.format("MMMM", locale=locale) == "august" 74 | assert d.format("MMM", locale=locale) == "aug." 75 | assert d.format("A", locale=locale) == "formiddag" 76 | assert d.format("Qo", locale=locale) == "3." 77 | assert d.format("Mo", locale=locale) == "8." 78 | assert d.format("Do", locale=locale) == "29." 79 | 80 | assert d.format("LT", locale=locale) == "07:03" 81 | assert d.format("LTS", locale=locale) == "07:03:06" 82 | assert d.format("L", locale=locale) == "29.08.2016" 83 | assert d.format("LL", locale=locale) == "29. august 2016" 84 | assert d.format("LLL", locale=locale) == "29. august 2016 07:03" 85 | assert d.format("LLLL", locale=locale) == "måndag 29. august 2016 07:03" 86 | -------------------------------------------------------------------------------- /tests/localization/test_ru.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pendulum 4 | 5 | 6 | locale = "ru" 7 | 8 | 9 | def test_diff_for_humans(): 10 | with pendulum.travel_to(pendulum.datetime(2016, 8, 29), freeze=True): 11 | diff_for_humans() 12 | 13 | 14 | def diff_for_humans(): 15 | d = pendulum.now().subtract(seconds=1) 16 | assert d.diff_for_humans(locale=locale) == "1 секунду назад" 17 | 18 | d = pendulum.now().subtract(seconds=2) 19 | assert d.diff_for_humans(locale=locale) == "2 секунды назад" 20 | 21 | d = pendulum.now().subtract(seconds=5) 22 | assert d.diff_for_humans(locale=locale) == "5 секунд назад" 23 | 24 | d = pendulum.now().subtract(seconds=21) 25 | assert d.diff_for_humans(locale=locale) == "21 секунду назад" 26 | 27 | d = pendulum.now().subtract(minutes=1) 28 | assert d.diff_for_humans(locale=locale) == "1 минуту назад" 29 | 30 | d = pendulum.now().subtract(minutes=2) 31 | assert d.diff_for_humans(locale=locale) == "2 минуты назад" 32 | 33 | d = pendulum.now().subtract(minutes=5) 34 | assert d.diff_for_humans(locale=locale) == "5 минут назад" 35 | 36 | d = pendulum.now().subtract(hours=1) 37 | assert d.diff_for_humans(locale=locale) == "1 час назад" 38 | 39 | d = pendulum.now().subtract(hours=2) 40 | assert d.diff_for_humans(locale=locale) == "2 часа назад" 41 | 42 | d = pendulum.now().subtract(hours=5) 43 | assert d.diff_for_humans(locale=locale) == "5 часов назад" 44 | 45 | d = pendulum.now().subtract(days=1) 46 | assert d.diff_for_humans(locale=locale) == "1 день назад" 47 | 48 | d = pendulum.now().subtract(days=2) 49 | assert d.diff_for_humans(locale=locale) == "2 дня назад" 50 | 51 | d = pendulum.now().subtract(days=5) 52 | assert d.diff_for_humans(locale=locale) == "5 дней назад" 53 | 54 | d = pendulum.now().subtract(weeks=1) 55 | assert d.diff_for_humans(locale=locale) == "1 неделю назад" 56 | 57 | d = pendulum.now().subtract(weeks=2) 58 | assert d.diff_for_humans(locale=locale) == "2 недели назад" 59 | 60 | d = pendulum.now().subtract(months=1) 61 | assert d.diff_for_humans(locale=locale) == "1 месяц назад" 62 | 63 | d = pendulum.now().subtract(months=2) 64 | assert d.diff_for_humans(locale=locale) == "2 месяца назад" 65 | 66 | d = pendulum.now().subtract(months=5) 67 | assert d.diff_for_humans(locale=locale) == "5 месяцев назад" 68 | 69 | d = pendulum.now().subtract(years=1) 70 | assert d.diff_for_humans(locale=locale) == "1 год назад" 71 | 72 | d = pendulum.now().subtract(years=2) 73 | assert d.diff_for_humans(locale=locale) == "2 года назад" 74 | 75 | d = pendulum.now().subtract(years=5) 76 | assert d.diff_for_humans(locale=locale) == "5 лет назад" 77 | 78 | d = pendulum.now().add(seconds=1) 79 | assert d.diff_for_humans(locale=locale) == "через 1 секунду" 80 | 81 | d = pendulum.now().add(seconds=1) 82 | d2 = pendulum.now() 83 | assert d.diff_for_humans(d2, locale=locale) == "1 секунда после" 84 | assert d2.diff_for_humans(d, locale=locale) == "1 секунда до" 85 | 86 | assert d.diff_for_humans(d2, True, locale=locale) == "1 секунда" 87 | assert d2.diff_for_humans(d.add(seconds=1), True, locale=locale) == "2 секунды" 88 | -------------------------------------------------------------------------------- /tests/localization/test_sv.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pendulum 4 | 5 | 6 | locale = "sv" 7 | 8 | 9 | def test_diff_for_humans(): 10 | with pendulum.travel_to(pendulum.datetime(2016, 8, 29), freeze=True): 11 | diff_for_humans() 12 | 13 | 14 | def diff_for_humans(): 15 | d = pendulum.now().subtract(seconds=1) 16 | assert d.diff_for_humans(locale=locale) == "för 1 sekund sedan" 17 | 18 | d = pendulum.now().subtract(seconds=2) 19 | assert d.diff_for_humans(locale=locale) == "för 2 sekunder sedan" 20 | 21 | d = pendulum.now().subtract(seconds=5) 22 | assert d.diff_for_humans(locale=locale) == "för 5 sekunder sedan" 23 | 24 | d = pendulum.now().subtract(seconds=21) 25 | assert d.diff_for_humans(locale=locale) == "för 21 sekunder sedan" 26 | 27 | d = pendulum.now().subtract(minutes=1) 28 | assert d.diff_for_humans(locale=locale) == "för 1 minut sedan" 29 | 30 | d = pendulum.now().subtract(minutes=2) 31 | assert d.diff_for_humans(locale=locale) == "för 2 minuter sedan" 32 | 33 | d = pendulum.now().subtract(minutes=5) 34 | assert d.diff_for_humans(locale=locale) == "för 5 minuter sedan" 35 | 36 | d = pendulum.now().subtract(hours=1) 37 | assert d.diff_for_humans(locale=locale) == "för 1 timme sedan" 38 | 39 | d = pendulum.now().subtract(hours=2) 40 | assert d.diff_for_humans(locale=locale) == "för 2 timmar sedan" 41 | 42 | d = pendulum.now().subtract(hours=5) 43 | assert d.diff_for_humans(locale=locale) == "för 5 timmar sedan" 44 | 45 | d = pendulum.now().subtract(days=1) 46 | assert d.diff_for_humans(locale=locale) == "för 1 dag sedan" 47 | 48 | d = pendulum.now().subtract(days=2) 49 | assert d.diff_for_humans(locale=locale) == "för 2 dagar sedan" 50 | 51 | d = pendulum.now().subtract(days=5) 52 | assert d.diff_for_humans(locale=locale) == "för 5 dagar sedan" 53 | 54 | d = pendulum.now().subtract(weeks=1) 55 | assert d.diff_for_humans(locale=locale) == "för 1 vecka sedan" 56 | 57 | d = pendulum.now().subtract(weeks=2) 58 | assert d.diff_for_humans(locale=locale) == "för 2 veckor sedan" 59 | 60 | d = pendulum.now().subtract(months=1) 61 | assert d.diff_for_humans(locale=locale) == "för 1 månad sedan" 62 | 63 | d = pendulum.now().subtract(months=2) 64 | assert d.diff_for_humans(locale=locale) == "för 2 månader sedan" 65 | 66 | d = pendulum.now().subtract(months=5) 67 | assert d.diff_for_humans(locale=locale) == "för 5 månader sedan" 68 | 69 | d = pendulum.now().subtract(years=1) 70 | assert d.diff_for_humans(locale=locale) == "för 1 år sedan" 71 | 72 | d = pendulum.now().subtract(years=2) 73 | assert d.diff_for_humans(locale=locale) == "för 2 år sedan" 74 | 75 | d = pendulum.now().subtract(years=5) 76 | assert d.diff_for_humans(locale=locale) == "för 5 år sedan" 77 | 78 | d = pendulum.now().add(seconds=1) 79 | assert d.diff_for_humans(locale=locale) == "om 1 sekund" 80 | 81 | d = pendulum.now().add(seconds=1) 82 | d2 = pendulum.now() 83 | assert d.diff_for_humans(d2, locale=locale) == "1 sekund efter" 84 | assert d2.diff_for_humans(d, locale=locale) == "1 sekund innan" 85 | 86 | assert d.diff_for_humans(d2, True, locale=locale) == "1 sekund" 87 | assert d2.diff_for_humans(d.add(seconds=1), True, locale=locale) == "2 sekunder" 88 | -------------------------------------------------------------------------------- /tests/localization/test_tr.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pendulum 4 | 5 | 6 | locale = "tr" 7 | 8 | 9 | def test_diff_for_humans(): 10 | with pendulum.travel_to(pendulum.datetime(2016, 8, 29), freeze=True): 11 | diff_for_humans() 12 | 13 | 14 | def diff_for_humans(): 15 | d = pendulum.now().subtract(seconds=1) 16 | assert d.diff_for_humans(locale=locale) == "1 saniye önce" 17 | 18 | d = pendulum.now().subtract(seconds=2) 19 | assert d.diff_for_humans(locale=locale) == "2 saniye önce" 20 | 21 | d = pendulum.now().subtract(minutes=1) 22 | assert d.diff_for_humans(locale=locale) == "1 dakika önce" 23 | 24 | d = pendulum.now().subtract(minutes=2) 25 | assert d.diff_for_humans(locale=locale) == "2 dakika önce" 26 | 27 | d = pendulum.now().subtract(hours=1) 28 | assert d.diff_for_humans(locale=locale) == "1 saat önce" 29 | 30 | d = pendulum.now().subtract(hours=2) 31 | assert d.diff_for_humans(locale=locale) == "2 saat önce" 32 | 33 | d = pendulum.now().subtract(days=1) 34 | assert d.diff_for_humans(locale=locale) == "1 gün önce" 35 | 36 | d = pendulum.now().subtract(days=2) 37 | assert d.diff_for_humans(locale=locale) == "2 gün önce" 38 | 39 | d = pendulum.now().subtract(weeks=1) 40 | assert d.diff_for_humans(locale=locale) == "1 hafta önce" 41 | 42 | d = pendulum.now().subtract(weeks=2) 43 | assert d.diff_for_humans(locale=locale) == "2 hafta önce" 44 | 45 | d = pendulum.now().subtract(months=1) 46 | assert d.diff_for_humans(locale=locale) == "1 ay önce" 47 | 48 | d = pendulum.now().subtract(months=2) 49 | assert d.diff_for_humans(locale=locale) == "2 ay önce" 50 | 51 | d = pendulum.now().subtract(years=1) 52 | assert d.diff_for_humans(locale=locale) == "1 yıl önce" 53 | 54 | d = pendulum.now().subtract(years=2) 55 | assert d.diff_for_humans(locale=locale) == "2 yıl önce" 56 | 57 | d = pendulum.now().add(seconds=1) 58 | assert d.diff_for_humans(locale=locale) == "1 saniye sonra" 59 | 60 | d = pendulum.now().add(seconds=1) 61 | d2 = pendulum.now() 62 | assert d.diff_for_humans(d2, locale=locale) == "1 saniye sonra" 63 | assert d2.diff_for_humans(d, locale=locale) == "1 saniye önce" 64 | 65 | assert d.diff_for_humans(d2, True, locale=locale) == "1 saniye" 66 | assert d2.diff_for_humans(d.add(seconds=1), True, locale=locale) == "2 saniye" 67 | -------------------------------------------------------------------------------- /tests/parsing/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-pendulum/pendulum/fc386be2623f711364c599df3e208eceb4dfa23b/tests/parsing/__init__.py -------------------------------------------------------------------------------- /tests/test_main.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from datetime import date 4 | from datetime import datetime 5 | from datetime import time 6 | 7 | import pytz 8 | 9 | from dateutil import tz 10 | 11 | import pendulum 12 | 13 | from pendulum import _safe_timezone 14 | from pendulum import timezone 15 | from pendulum.tz.timezone import Timezone 16 | 17 | 18 | def test_instance_with_naive_datetime_defaults_to_utc() -> None: 19 | now = pendulum.instance(datetime.now()) 20 | assert now.timezone_name == "UTC" 21 | 22 | 23 | def test_instance_with_aware_datetime() -> None: 24 | now = pendulum.instance(datetime.now(timezone("Europe/Paris"))) 25 | assert now.timezone_name == "Europe/Paris" 26 | 27 | 28 | def test_instance_with_aware_datetime_pytz() -> None: 29 | now = pendulum.instance(datetime.now(pytz.timezone("Europe/Paris"))) 30 | assert now.timezone_name == "Europe/Paris" 31 | 32 | 33 | def test_instance_with_aware_datetime_any_tzinfo() -> None: 34 | dt = datetime(2016, 8, 7, 12, 34, 56, tzinfo=tz.gettz("Europe/Paris")) 35 | now = pendulum.instance(dt) 36 | assert now.timezone_name == "+02:00" 37 | 38 | 39 | def test_instance_with_date() -> None: 40 | dt = pendulum.instance(date(2022, 12, 23)) 41 | 42 | assert isinstance(dt, pendulum.Date) 43 | 44 | 45 | def test_instance_with_naive_time() -> None: 46 | dt = pendulum.instance(time(12, 34, 56, 123456)) 47 | 48 | assert isinstance(dt, pendulum.Time) 49 | 50 | 51 | def test_instance_with_aware_time() -> None: 52 | dt = pendulum.instance(time(12, 34, 56, 123456, tzinfo=timezone("Europe/Paris"))) 53 | 54 | assert isinstance(dt, pendulum.Time) 55 | assert isinstance(dt.tzinfo, Timezone) 56 | assert dt.tzinfo.name == "Europe/Paris" 57 | 58 | 59 | def test_safe_timezone_with_tzinfo_objects() -> None: 60 | tz = _safe_timezone(pytz.timezone("Europe/Paris")) 61 | 62 | assert isinstance(tz, Timezone) 63 | assert tz.name == "Europe/Paris" 64 | -------------------------------------------------------------------------------- /tests/testing/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-pendulum/pendulum/fc386be2623f711364c599df3e208eceb4dfa23b/tests/testing/__init__.py -------------------------------------------------------------------------------- /tests/testing/test_time_travel.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from time import sleep 4 | from typing import TYPE_CHECKING 5 | 6 | import pytest 7 | 8 | import pendulum 9 | 10 | from pendulum.utils._compat import PYPY 11 | 12 | 13 | if TYPE_CHECKING: 14 | from collections.abc import Generator 15 | 16 | 17 | @pytest.fixture(autouse=True) 18 | def setup() -> Generator[None, None, None]: 19 | pendulum.travel_back() 20 | 21 | yield 22 | 23 | pendulum.travel_back() 24 | 25 | 26 | @pytest.mark.skipif(PYPY, reason="Time travelling not available on PyPy") 27 | def test_travel() -> None: 28 | now = pendulum.now() 29 | 30 | pendulum.travel(minutes=5) 31 | 32 | assert pendulum.now().diff_for_humans(now) == "5 minutes after" 33 | 34 | 35 | @pytest.mark.skipif(PYPY, reason="Time travelling not available on PyPy") 36 | def test_travel_with_frozen_time() -> None: 37 | pendulum.travel(minutes=5, freeze=True) 38 | 39 | now = pendulum.now() 40 | 41 | sleep(0.01) 42 | 43 | assert now == pendulum.now() 44 | 45 | 46 | @pytest.mark.skipif(PYPY, reason="Time travelling not available on PyPy") 47 | def test_travel_to() -> None: 48 | dt = pendulum.datetime(2022, 1, 19, tz="local") 49 | 50 | pendulum.travel_to(dt) 51 | 52 | assert pendulum.now().date() == dt.date() 53 | 54 | 55 | @pytest.mark.skipif(PYPY, reason="Time travelling not available on PyPy") 56 | def test_freeze() -> None: 57 | pendulum.freeze() 58 | 59 | pendulum.travel(minutes=5) 60 | 61 | assert pendulum.now() == pendulum.now() 62 | 63 | pendulum.travel_back() 64 | 65 | pendulum.travel(minutes=5) 66 | 67 | now = pendulum.now() 68 | 69 | sleep(0.01) 70 | 71 | assert now != pendulum.now() 72 | 73 | pendulum.freeze() 74 | 75 | assert pendulum.now() == pendulum.now() 76 | 77 | pendulum.travel_back() 78 | 79 | with pendulum.freeze(): 80 | assert pendulum.now() == pendulum.now() 81 | 82 | now = pendulum.now() 83 | 84 | sleep(0.01) 85 | 86 | assert now != pendulum.now() 87 | -------------------------------------------------------------------------------- /tests/time/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-pendulum/pendulum/fc386be2623f711364c599df3e208eceb4dfa23b/tests/time/__init__.py -------------------------------------------------------------------------------- /tests/time/test_add.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from datetime import timedelta 4 | 5 | import pytest 6 | 7 | import pendulum 8 | 9 | 10 | def test_add_hours_positive(): 11 | assert pendulum.time(12, 34, 56).add(hours=1).hour == 13 12 | 13 | 14 | def test_add_hours_zero(): 15 | assert pendulum.time(12, 34, 56).add(hours=0).hour == 12 16 | 17 | 18 | def test_add_hours_negative(): 19 | assert pendulum.time(12, 34, 56).add(hours=-1).hour == 11 20 | 21 | 22 | def test_add_minutes_positive(): 23 | assert pendulum.time(12, 34, 56).add(minutes=1).minute == 35 24 | 25 | 26 | def test_add_minutes_zero(): 27 | assert pendulum.time(12, 34, 56).add(minutes=0).minute == 34 28 | 29 | 30 | def test_add_minutes_negative(): 31 | assert pendulum.time(12, 34, 56).add(minutes=-1).minute == 33 32 | 33 | 34 | def test_add_seconds_positive(): 35 | assert pendulum.time(12, 34, 56).add(seconds=1).second == 57 36 | 37 | 38 | def test_add_seconds_zero(): 39 | assert pendulum.time(12, 34, 56).add(seconds=0).second == 56 40 | 41 | 42 | def test_add_seconds_negative(): 43 | assert pendulum.time(12, 34, 56).add(seconds=-1).second == 55 44 | 45 | 46 | def test_add_timedelta(): 47 | delta = timedelta(seconds=45, microseconds=123456) 48 | d = pendulum.time(3, 12, 15, 654321) 49 | 50 | d = d.add_timedelta(delta) 51 | assert d.minute == 13 52 | assert d.second == 0 53 | assert d.microsecond == 777777 54 | 55 | d = pendulum.time(3, 12, 15, 654321) 56 | 57 | d = d + delta 58 | assert d.minute == 13 59 | assert d.second == 0 60 | assert d.microsecond == 777777 61 | 62 | 63 | def test_add_timedelta_with_days(): 64 | delta = timedelta(days=3, seconds=45, microseconds=123456) 65 | d = pendulum.time(3, 12, 15, 654321) 66 | 67 | with pytest.raises(TypeError): 68 | d.add_timedelta(delta) 69 | 70 | 71 | def test_addition_invalid_type(): 72 | d = pendulum.time(3, 12, 15, 654321) 73 | 74 | with pytest.raises(TypeError): 75 | d + 3 76 | 77 | with pytest.raises(TypeError): 78 | 3 + d 79 | -------------------------------------------------------------------------------- /tests/time/test_behavior.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pickle 4 | 5 | from datetime import time 6 | 7 | import pytest 8 | 9 | import pendulum 10 | 11 | from pendulum import Time 12 | 13 | 14 | @pytest.fixture() 15 | def p(): 16 | return pendulum.Time(12, 34, 56, 123456, tzinfo=pendulum.timezone("Europe/Paris")) 17 | 18 | 19 | @pytest.fixture() 20 | def d(): 21 | return time(12, 34, 56, 123456, tzinfo=pendulum.timezone("Europe/Paris")) 22 | 23 | 24 | def test_hash(p, d): 25 | assert hash(d) == hash(p) 26 | dt1 = Time(12, 34, 57, 123456) 27 | 28 | assert hash(p) != hash(dt1) 29 | 30 | 31 | def test_pickle(): 32 | dt1 = Time(12, 34, 56, 123456) 33 | s = pickle.dumps(dt1) 34 | dt2 = pickle.loads(s) 35 | 36 | assert dt2 == dt1 37 | 38 | 39 | def test_utcoffset(p, d): 40 | assert d.utcoffset() == p.utcoffset() 41 | 42 | 43 | def test_dst(p, d): 44 | assert d.dst() == p.dst() 45 | 46 | 47 | def test_tzname(p, d): 48 | assert d.tzname() == p.tzname() 49 | assert Time(12, 34, 56, 123456).tzname() == time(12, 34, 56, 123456).tzname() 50 | -------------------------------------------------------------------------------- /tests/time/test_construct.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pendulum 4 | 5 | from tests.conftest import assert_time 6 | 7 | 8 | def test_init(): 9 | t = pendulum.time(12, 34, 56, 123456) 10 | 11 | assert_time(t, 12, 34, 56, 123456) 12 | 13 | 14 | def test_init_with_missing_values(): 15 | t = pendulum.time(12, 34, 56) 16 | assert_time(t, 12, 34, 56, 0) 17 | 18 | t = pendulum.time(12, 34) 19 | assert_time(t, 12, 34, 0, 0) 20 | 21 | t = pendulum.time(12) 22 | assert_time(t, 12, 0, 0, 0) 23 | -------------------------------------------------------------------------------- /tests/time/test_fluent_setters.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from pendulum import Time 4 | from tests.conftest import assert_time 5 | 6 | 7 | def test_replace(): 8 | t = Time(12, 34, 56, 123456) 9 | t = t.replace(1, 2, 3, 654321) 10 | 11 | assert isinstance(t, Time) 12 | assert_time(t, 1, 2, 3, 654321) 13 | -------------------------------------------------------------------------------- /tests/time/test_strings.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from pendulum import Time 4 | 5 | 6 | def test_to_string(): 7 | d = Time(1, 2, 3) 8 | assert str(d) == "01:02:03" 9 | d = Time(1, 2, 3, 123456) 10 | assert str(d) == "01:02:03.123456" 11 | 12 | 13 | def test_repr(): 14 | d = Time(1, 2, 3) 15 | assert repr(d) == "Time(1, 2, 3)" 16 | 17 | d = Time(1, 2, 3, 123456) 18 | assert repr(d) == "Time(1, 2, 3, 123456)" 19 | 20 | 21 | def test_format_with_locale(): 22 | d = Time(14, 15, 16) 23 | assert d.format("hh:mm:ss A", locale="fr") == "02:15:16 PM" 24 | 25 | 26 | def test_strftime(): 27 | d = Time(14, 15, 16) 28 | assert d.strftime("%H") == "14" 29 | 30 | 31 | def test_for_json(): 32 | d = Time(14, 15, 16) 33 | assert d.for_json() == "14:15:16" 34 | 35 | 36 | def test_format(): 37 | d = Time(14, 15, 16) 38 | assert f"{d}" == "14:15:16" 39 | assert f"{d:mm}" == "15" 40 | -------------------------------------------------------------------------------- /tests/time/test_sub.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from datetime import time 4 | from datetime import timedelta 5 | 6 | import pytest 7 | import pytz 8 | 9 | import pendulum 10 | 11 | from pendulum import Time 12 | from tests.conftest import assert_duration 13 | 14 | 15 | def test_sub_hours_positive(): 16 | assert Time(0, 0, 0).subtract(hours=1).hour == 23 17 | 18 | 19 | def test_sub_hours_zero(): 20 | assert Time(0, 0, 0).subtract(hours=0).hour == 0 21 | 22 | 23 | def test_sub_hours_negative(): 24 | assert Time(0, 0, 0).subtract(hours=-1).hour == 1 25 | 26 | 27 | def test_sub_minutes_positive(): 28 | assert Time(0, 0, 0).subtract(minutes=1).minute == 59 29 | 30 | 31 | def test_sub_minutes_zero(): 32 | assert Time(0, 0, 0).subtract(minutes=0).minute == 0 33 | 34 | 35 | def test_sub_minutes_negative(): 36 | assert Time(0, 0, 0).subtract(minutes=-1).minute == 1 37 | 38 | 39 | def test_sub_seconds_positive(): 40 | assert Time(0, 0, 0).subtract(seconds=1).second == 59 41 | 42 | 43 | def test_sub_seconds_zero(): 44 | assert Time(0, 0, 0).subtract(seconds=0).second == 0 45 | 46 | 47 | def test_sub_seconds_negative(): 48 | assert Time(0, 0, 0).subtract(seconds=-1).second == 1 49 | 50 | 51 | def test_subtract_timedelta(): 52 | delta = timedelta(seconds=16, microseconds=654321) 53 | d = Time(3, 12, 15, 777777) 54 | 55 | d = d.subtract_timedelta(delta) 56 | assert d.minute == 11 57 | assert d.second == 59 58 | assert d.microsecond == 123456 59 | 60 | d = Time(3, 12, 15, 777777) 61 | 62 | d = d - delta 63 | assert d.minute == 11 64 | assert d.second == 59 65 | assert d.microsecond == 123456 66 | 67 | 68 | def test_add_timedelta_with_days(): 69 | delta = timedelta(days=3, seconds=45, microseconds=123456) 70 | d = Time(3, 12, 15, 654321) 71 | 72 | with pytest.raises(TypeError): 73 | d.subtract_timedelta(delta) 74 | 75 | 76 | def test_subtract_invalid_type(): 77 | d = Time(0, 0, 0) 78 | 79 | with pytest.raises(TypeError): 80 | d - "ab" 81 | 82 | with pytest.raises(TypeError): 83 | "ab" - d 84 | 85 | 86 | def test_subtract_time(): 87 | t = Time(12, 34, 56) 88 | t1 = Time(1, 1, 1) 89 | t2 = time(1, 1, 1) 90 | t3 = time(1, 1, 1, tzinfo=pytz.timezone("Europe/Paris")) 91 | 92 | diff = t - t1 93 | assert isinstance(diff, pendulum.Duration) 94 | assert_duration(diff, 0, hours=11, minutes=33, seconds=55) 95 | 96 | diff = t1 - t 97 | assert isinstance(diff, pendulum.Duration) 98 | assert_duration(diff, 0, hours=-11, minutes=-33, seconds=-55) 99 | 100 | diff = t - t2 101 | assert isinstance(diff, pendulum.Duration) 102 | assert_duration(diff, 0, hours=11, minutes=33, seconds=55) 103 | 104 | diff = t2 - t 105 | assert isinstance(diff, pendulum.Duration) 106 | assert_duration(diff, 0, hours=-11, minutes=-33, seconds=-55) 107 | 108 | with pytest.raises(TypeError): 109 | t - t3 110 | 111 | with pytest.raises(TypeError): 112 | t3 - t 113 | -------------------------------------------------------------------------------- /tests/tz/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-pendulum/pendulum/fc386be2623f711364c599df3e208eceb4dfa23b/tests/tz/__init__.py -------------------------------------------------------------------------------- /tests/tz/test_helpers.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pytest 4 | 5 | from pendulum import timezone 6 | from pendulum.tz.exceptions import InvalidTimezone 7 | from pendulum.tz.timezone import FixedTimezone 8 | from pendulum.tz.timezone import Timezone 9 | 10 | 11 | def test_timezone_with_name(): 12 | tz = timezone("Europe/Paris") 13 | 14 | assert isinstance(tz, Timezone) 15 | assert tz.name == "Europe/Paris" 16 | 17 | 18 | def test_timezone_with_invalid_name(): 19 | with pytest.raises(InvalidTimezone): 20 | timezone("Invalid") 21 | 22 | 23 | def test_timezone_with_offset(): 24 | tz = timezone(-19800) 25 | 26 | assert isinstance(tz, FixedTimezone) 27 | assert tz.name == "-05:30" 28 | -------------------------------------------------------------------------------- /tests/tz/test_local_timezone.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import os 4 | import sys 5 | 6 | import pytest 7 | 8 | from pendulum.tz.local_timezone import _get_unix_timezone 9 | from pendulum.tz.local_timezone import _get_windows_timezone 10 | 11 | 12 | @pytest.mark.skipif( 13 | sys.platform == "win32", reason="Test only available for UNIX systems" 14 | ) 15 | def test_unix_symlink(): 16 | # A ZONE setting in the target path of a symbolic linked localtime, 17 | # f ex systemd distributions 18 | local_path = os.path.join(os.path.split(__file__)[0], "..") 19 | tz = _get_unix_timezone(_root=os.path.join(local_path, "fixtures", "tz", "symlink")) 20 | 21 | assert tz.name == "Europe/Paris" 22 | 23 | 24 | @pytest.mark.skipif( 25 | sys.platform == "win32", reason="Test only available for UNIX systems" 26 | ) 27 | def test_unix_clock(): 28 | # A ZONE setting in the target path of a symbolic linked localtime, 29 | # f ex systemd distributions 30 | local_path = os.path.join(os.path.split(__file__)[0], "..") 31 | tz = _get_unix_timezone(_root=os.path.join(local_path, "fixtures", "tz", "clock")) 32 | 33 | assert tz.name == "Europe/Zurich" 34 | 35 | 36 | @pytest.mark.skipif(sys.platform != "win32", reason="Test only available for Windows") 37 | def test_windows_timezone(): 38 | timezone = _get_windows_timezone() 39 | 40 | assert timezone is not None 41 | 42 | 43 | @pytest.mark.skipif( 44 | sys.platform == "win32", reason="Test only available for UNIX systems" 45 | ) 46 | def test_unix_etc_timezone_dir(): 47 | # Should not fail if `/etc/timezone` is a folder 48 | local_path = os.path.join(os.path.split(__file__)[0], "..") 49 | root_path = os.path.join(local_path, "fixtures", "tz", "timezone_dir") 50 | tz = _get_unix_timezone(_root=root_path) 51 | 52 | assert tz.name == "Europe/Paris" 53 | -------------------------------------------------------------------------------- /tests/tz/test_timezones.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pytest 4 | 5 | import pendulum 6 | 7 | 8 | def test_timezones(): 9 | zones = pendulum.timezones() 10 | 11 | assert "America/Argentina/Buenos_Aires" in zones 12 | 13 | 14 | @pytest.mark.parametrize("zone", list(pendulum.timezones())) 15 | def test_timezones_are_loadable(zone): 16 | pendulum.timezone(zone) 17 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | isolated_build = true 3 | envlist = py37, py38, py39, py310, pypy3 4 | 5 | [testenv] 6 | whitelist_externals = poetry 7 | commands = 8 | poetry install -v 9 | poetry run pytest tests/ 10 | 11 | [testenv:pypy] 12 | whitelist_externals = 13 | bash 14 | poetry 15 | skip_install = true 16 | commands = 17 | poetry install -v 18 | poetry run pytest tests/ 19 | --------------------------------------------------------------------------------