From 74bc0a1d2583ca28994bd8dca7929ea5b061c25f Mon Sep 17 00:00:00 2001 From: Anish Nyayachavadi <55898433+anishnya@users.noreply.github.com> Date: Mon, 27 Dec 2021 16:07:58 -0500 Subject: [PATCH 01/44] Dehumanize Support for Czech and Slovak Locales (#1079) --- arrow/constants.py | 4 ++++ arrow/locales.py | 42 ++++++++++++++++++++++++++++-------------- tests/test_arrow.py | 36 ++++++++++++++++++++++++++++++++++++ tests/test_locales.py | 28 ++++++++++++++-------------- 4 files changed, 82 insertions(+), 28 deletions(-) diff --git a/arrow/constants.py b/arrow/constants.py index d26bc0d8..1189d07c 100644 --- a/arrow/constants.py +++ b/arrow/constants.py @@ -110,6 +110,10 @@ "da-dk", "ml", "hi", + "cs", + "cs-cz", + "sk", + "sk-sk", "fa", "fa-ir", "mr", diff --git a/arrow/locales.py b/arrow/locales.py index 6b1627e7..ddbecb77 100644 --- a/arrow/locales.py +++ b/arrow/locales.py @@ -3067,44 +3067,51 @@ class CzechLocale(Locale): timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]] = { "now": "Teď", - "second": {"past": "vteřina", "future": "vteřina", "zero": "vteřina"}, + "second": {"past": "vteřina", "future": "vteřina"}, "seconds": { + "zero": "vteřina", "past": "{0} sekundami", "future-singular": "{0} sekundy", "future-paucal": "{0} sekund", }, - "minute": {"past": "minutou", "future": "minutu", "zero": "{0} minut"}, + "minute": {"past": "minutou", "future": "minutu"}, "minutes": { + "zero": "{0} minut", "past": "{0} minutami", "future-singular": "{0} minuty", "future-paucal": "{0} minut", }, - "hour": {"past": "hodinou", "future": "hodinu", "zero": "{0} hodin"}, + "hour": {"past": "hodinou", "future": "hodinu"}, "hours": { + "zero": "{0} hodin", "past": "{0} hodinami", "future-singular": "{0} hodiny", "future-paucal": "{0} hodin", }, - "day": {"past": "dnem", "future": "den", "zero": "{0} dnů"}, + "day": {"past": "dnem", "future": "den"}, "days": { + "zero": "{0} dnů", "past": "{0} dny", "future-singular": "{0} dny", "future-paucal": "{0} dnů", }, - "week": {"past": "týdnem", "future": "týden", "zero": "{0} týdnů"}, + "week": {"past": "týdnem", "future": "týden"}, "weeks": { + "zero": "{0} týdnů", "past": "{0} týdny", "future-singular": "{0} týdny", "future-paucal": "{0} týdnů", }, - "month": {"past": "měsícem", "future": "měsíc", "zero": "{0} měsíců"}, + "month": {"past": "měsícem", "future": "měsíc"}, "months": { + "zero": "{0} měsíců", "past": "{0} měsíci", "future-singular": "{0} měsíce", "future-paucal": "{0} měsíců", }, - "year": {"past": "rokem", "future": "rok", "zero": "{0} let"}, + "year": {"past": "rokem", "future": "rok"}, "years": { + "zero": "{0} let", "past": "{0} lety", "future-singular": "{0} roky", "future-paucal": "{0} let", @@ -3190,44 +3197,51 @@ class SlovakLocale(Locale): timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]] = { "now": "Teraz", - "second": {"past": "sekundou", "future": "sekundu", "zero": "{0} sekúnd"}, + "second": {"past": "sekundou", "future": "sekundu"}, "seconds": { + "zero": "{0} sekúnd", "past": "{0} sekundami", "future-singular": "{0} sekundy", "future-paucal": "{0} sekúnd", }, - "minute": {"past": "minútou", "future": "minútu", "zero": "{0} minút"}, + "minute": {"past": "minútou", "future": "minútu"}, "minutes": { + "zero": "{0} minút", "past": "{0} minútami", "future-singular": "{0} minúty", "future-paucal": "{0} minút", }, - "hour": {"past": "hodinou", "future": "hodinu", "zero": "{0} hodín"}, + "hour": {"past": "hodinou", "future": "hodinu"}, "hours": { + "zero": "{0} hodín", "past": "{0} hodinami", "future-singular": "{0} hodiny", "future-paucal": "{0} hodín", }, - "day": {"past": "dňom", "future": "deň", "zero": "{0} dní"}, + "day": {"past": "dňom", "future": "deň"}, "days": { + "zero": "{0} dní", "past": "{0} dňami", "future-singular": "{0} dni", "future-paucal": "{0} dní", }, - "week": {"past": "týždňom", "future": "týždeň", "zero": "{0} týždňov"}, + "week": {"past": "týždňom", "future": "týždeň"}, "weeks": { + "zero": "{0} týždňov", "past": "{0} týždňami", "future-singular": "{0} týždne", "future-paucal": "{0} týždňov", }, - "month": {"past": "mesiacom", "future": "mesiac", "zero": "{0} mesiacov"}, + "month": {"past": "mesiacom", "future": "mesiac"}, "months": { + "zero": "{0} mesiacov", "past": "{0} mesiacmi", "future-singular": "{0} mesiace", "future-paucal": "{0} mesiacov", }, - "year": {"past": "rokom", "future": "rok", "zero": "{0} rokov"}, + "year": {"past": "rokom", "future": "rok"}, "years": { + "zero": "{0} rokov", "past": "{0} rokmi", "future-singular": "{0} roky", "future-paucal": "{0} rokov", diff --git a/tests/test_arrow.py b/tests/test_arrow.py index a2f08813..60f3a64b 100644 --- a/tests/test_arrow.py +++ b/tests/test_arrow.py @@ -2410,6 +2410,10 @@ def locale_list_no_weeks() -> List[str]: "da-dk", "ml", "hi", + "cs", + "cs-cz", + "sk", + "sk-sk", "fa", "fa-ir", "mr", @@ -2511,6 +2515,10 @@ def locale_list_with_weeks() -> List[str]: "pt", "pt-pt", "pt-br", + "cs", + "cs-cz", + "sk", + "sk-sk", "tl", "tl-ph", "vi", @@ -2954,6 +2962,34 @@ def test_slavic_locales(self, slavic_locales: List[str]): assert arw.dehumanize(past_string, locale=lang) == past assert arw.dehumanize(future_string, locale=lang) == future + def test_czech_slovak(self): + + # Relevant units for Slavic locale plural logic + units = [ + 0, + 1, + 2, + 5, + ] + + # Only need to test on seconds as logic holds for all slavic plural units + for lang in ["cs"]: + for unit in units: + arw = arrow.Arrow(2000, 2, 18, 1, 50, 30) + + past = arw.shift(minutes=-1 * unit, days=-1) + future = arw.shift(minutes=unit, days=1) + + past_string = past.humanize( + arw, locale=lang, granularity=["minute", "day"] + ) + future_string = future.humanize( + arw, locale=lang, granularity=["minute", "day"] + ) + + assert arw.dehumanize(past_string, locale=lang) == past + assert arw.dehumanize(future_string, locale=lang) == future + class TestArrowIsBetween: def test_start_before_end(self): diff --git a/tests/test_locales.py b/tests/test_locales.py index 54f99ef0..54536c0a 100644 --- a/tests/test_locales.py +++ b/tests/test_locales.py @@ -613,8 +613,8 @@ def test_format_timeframe(self): # Second(s) assert self.locale._format_timeframe("second", -1) == "vteřina" - assert self.locale._format_timeframe("second", 0) == "vteřina" assert self.locale._format_timeframe("second", 1) == "vteřina" + assert self.locale._format_timeframe("seconds", 0) == "vteřina" assert self.locale._format_timeframe("seconds", -2) == "2 sekundami" assert self.locale._format_timeframe("seconds", -5) == "5 sekundami" assert self.locale._format_timeframe("seconds", 2) == "2 sekundy" @@ -622,8 +622,8 @@ def test_format_timeframe(self): # Minute(s) assert self.locale._format_timeframe("minute", -1) == "minutou" - assert self.locale._format_timeframe("minute", 0) == "0 minut" assert self.locale._format_timeframe("minute", 1) == "minutu" + assert self.locale._format_timeframe("minutes", 0) == "0 minut" assert self.locale._format_timeframe("minutes", -2) == "2 minutami" assert self.locale._format_timeframe("minutes", -5) == "5 minutami" assert self.locale._format_timeframe("minutes", 2) == "2 minuty" @@ -631,8 +631,8 @@ def test_format_timeframe(self): # Hour(s) assert self.locale._format_timeframe("hour", -1) == "hodinou" - assert self.locale._format_timeframe("hour", 0) == "0 hodin" assert self.locale._format_timeframe("hour", 1) == "hodinu" + assert self.locale._format_timeframe("hours", 0) == "0 hodin" assert self.locale._format_timeframe("hours", -2) == "2 hodinami" assert self.locale._format_timeframe("hours", -5) == "5 hodinami" assert self.locale._format_timeframe("hours", 2) == "2 hodiny" @@ -640,8 +640,8 @@ def test_format_timeframe(self): # Day(s) assert self.locale._format_timeframe("day", -1) == "dnem" - assert self.locale._format_timeframe("day", 0) == "0 dnů" assert self.locale._format_timeframe("day", 1) == "den" + assert self.locale._format_timeframe("days", 0) == "0 dnů" assert self.locale._format_timeframe("days", -2) == "2 dny" assert self.locale._format_timeframe("days", -5) == "5 dny" assert self.locale._format_timeframe("days", 2) == "2 dny" @@ -649,8 +649,8 @@ def test_format_timeframe(self): # Weeks(s) assert self.locale._format_timeframe("week", -1) == "týdnem" - assert self.locale._format_timeframe("week", 0) == "0 týdnů" assert self.locale._format_timeframe("week", 1) == "týden" + assert self.locale._format_timeframe("weeks", 0) == "0 týdnů" assert self.locale._format_timeframe("weeks", -2) == "2 týdny" assert self.locale._format_timeframe("weeks", -5) == "5 týdny" assert self.locale._format_timeframe("weeks", 2) == "2 týdny" @@ -658,8 +658,8 @@ def test_format_timeframe(self): # Month(s) assert self.locale._format_timeframe("month", -1) == "měsícem" - assert self.locale._format_timeframe("month", 0) == "0 měsíců" assert self.locale._format_timeframe("month", 1) == "měsíc" + assert self.locale._format_timeframe("months", 0) == "0 měsíců" assert self.locale._format_timeframe("months", -2) == "2 měsíci" assert self.locale._format_timeframe("months", -5) == "5 měsíci" assert self.locale._format_timeframe("months", 2) == "2 měsíce" @@ -667,8 +667,8 @@ def test_format_timeframe(self): # Year(s) assert self.locale._format_timeframe("year", -1) == "rokem" - assert self.locale._format_timeframe("year", 0) == "0 let" assert self.locale._format_timeframe("year", 1) == "rok" + assert self.locale._format_timeframe("years", 0) == "0 let" assert self.locale._format_timeframe("years", -2) == "2 lety" assert self.locale._format_timeframe("years", -5) == "5 lety" assert self.locale._format_timeframe("years", 2) == "2 roky" @@ -697,7 +697,7 @@ def test_format_timeframe(self): assert self.locale._format_timeframe("seconds", -5) == "5 sekundami" assert self.locale._format_timeframe("seconds", -2) == "2 sekundami" assert self.locale._format_timeframe("second", -1) == "sekundou" - assert self.locale._format_timeframe("second", 0) == "0 sekúnd" + assert self.locale._format_timeframe("seconds", 0) == "0 sekúnd" assert self.locale._format_timeframe("second", 1) == "sekundu" assert self.locale._format_timeframe("seconds", 2) == "2 sekundy" assert self.locale._format_timeframe("seconds", 5) == "5 sekúnd" @@ -705,7 +705,7 @@ def test_format_timeframe(self): assert self.locale._format_timeframe("minutes", -5) == "5 minútami" assert self.locale._format_timeframe("minutes", -2) == "2 minútami" assert self.locale._format_timeframe("minute", -1) == "minútou" - assert self.locale._format_timeframe("minute", 0) == "0 minút" + assert self.locale._format_timeframe("minutes", 0) == "0 minút" assert self.locale._format_timeframe("minute", 1) == "minútu" assert self.locale._format_timeframe("minutes", 2) == "2 minúty" assert self.locale._format_timeframe("minutes", 5) == "5 minút" @@ -713,7 +713,7 @@ def test_format_timeframe(self): assert self.locale._format_timeframe("hours", -5) == "5 hodinami" assert self.locale._format_timeframe("hours", -2) == "2 hodinami" assert self.locale._format_timeframe("hour", -1) == "hodinou" - assert self.locale._format_timeframe("hour", 0) == "0 hodín" + assert self.locale._format_timeframe("hours", 0) == "0 hodín" assert self.locale._format_timeframe("hour", 1) == "hodinu" assert self.locale._format_timeframe("hours", 2) == "2 hodiny" assert self.locale._format_timeframe("hours", 5) == "5 hodín" @@ -721,7 +721,7 @@ def test_format_timeframe(self): assert self.locale._format_timeframe("days", -5) == "5 dňami" assert self.locale._format_timeframe("days", -2) == "2 dňami" assert self.locale._format_timeframe("day", -1) == "dňom" - assert self.locale._format_timeframe("day", 0) == "0 dní" + assert self.locale._format_timeframe("days", 0) == "0 dní" assert self.locale._format_timeframe("day", 1) == "deň" assert self.locale._format_timeframe("days", 2) == "2 dni" assert self.locale._format_timeframe("days", 5) == "5 dní" @@ -729,7 +729,7 @@ def test_format_timeframe(self): assert self.locale._format_timeframe("weeks", -5) == "5 týždňami" assert self.locale._format_timeframe("weeks", -2) == "2 týždňami" assert self.locale._format_timeframe("week", -1) == "týždňom" - assert self.locale._format_timeframe("week", 0) == "0 týždňov" + assert self.locale._format_timeframe("weeks", 0) == "0 týždňov" assert self.locale._format_timeframe("week", 1) == "týždeň" assert self.locale._format_timeframe("weeks", 2) == "2 týždne" assert self.locale._format_timeframe("weeks", 5) == "5 týždňov" @@ -737,7 +737,7 @@ def test_format_timeframe(self): assert self.locale._format_timeframe("months", -5) == "5 mesiacmi" assert self.locale._format_timeframe("months", -2) == "2 mesiacmi" assert self.locale._format_timeframe("month", -1) == "mesiacom" - assert self.locale._format_timeframe("month", 0) == "0 mesiacov" + assert self.locale._format_timeframe("months", 0) == "0 mesiacov" assert self.locale._format_timeframe("month", 1) == "mesiac" assert self.locale._format_timeframe("months", 2) == "2 mesiace" assert self.locale._format_timeframe("months", 5) == "5 mesiacov" @@ -745,7 +745,7 @@ def test_format_timeframe(self): assert self.locale._format_timeframe("years", -5) == "5 rokmi" assert self.locale._format_timeframe("years", -2) == "2 rokmi" assert self.locale._format_timeframe("year", -1) == "rokom" - assert self.locale._format_timeframe("year", 0) == "0 rokov" + assert self.locale._format_timeframe("years", 0) == "0 rokov" assert self.locale._format_timeframe("year", 1) == "rok" assert self.locale._format_timeframe("years", 2) == "2 roky" assert self.locale._format_timeframe("years", 5) == "5 rokov" From 774ac01303d9b6d5f0bb321b6965af10ac050ff8 Mon Sep 17 00:00:00 2001 From: Jad Chaar Date: Fri, 31 Dec 2021 18:14:02 -0500 Subject: [PATCH 02/44] Break up requirements.txt into multiple requirements files (#1080) * Break up requirements.txt into multiple requirements files. * Fix linting --- .github/workflows/continuous_integration.yml | 72 ++++--------------- .pre-commit-config.yaml | 24 ++++--- Makefile | 15 ++-- arrow/locales.py | 4 +- requirements/requirements-docs.txt | 5 ++ .../requirements-tests.txt | 11 ++- requirements/requirements.txt | 2 + tox.ini | 15 ++-- 8 files changed, 59 insertions(+), 89 deletions(-) create mode 100644 requirements/requirements-docs.txt rename requirements-dev.txt => requirements/requirements-tests.txt (51%) create mode 100644 requirements/requirements.txt diff --git a/.github/workflows/continuous_integration.yml b/.github/workflows/continuous_integration.yml index 140bf446..d60e4bd3 100644 --- a/.github/workflows/continuous_integration.yml +++ b/.github/workflows/continuous_integration.yml @@ -12,7 +12,6 @@ jobs: test: name: ${{ matrix.os }} (${{ matrix.python-version }}) runs-on: ${{ matrix.os }} - strategy: fail-fast: false matrix: @@ -22,56 +21,31 @@ jobs: # pypy3 randomly fails on Windows builds - os: windows-latest python-version: "pypy-3.7" - + include: + - os: ubuntu-latest + path: ~/.cache/pip + - os: macos-latest + path: ~/Library/Caches/pip + - os: windows-latest + path: ~\AppData\Local\pip\Cache steps: - # Check out latest code - uses: actions/checkout@v2 - - # Configure pip cache - - name: Cache pip (Linux) - uses: actions/cache@v2 - if: startsWith(runner.os, 'Linux') - with: - path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements-dev.txt') }} - restore-keys: | - ${{ runner.os }}-pip- - - - name: Cache pip (macOS) - uses: actions/cache@v2 - if: startsWith(runner.os, 'macOS') - with: - path: ~/Library/Caches/pip - key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements-dev.txt') }} - restore-keys: | - ${{ runner.os }}-pip- - - - name: Cache pip (Windows) + - name: Cache pip uses: actions/cache@v2 - if: startsWith(runner.os, 'Windows') with: - path: ~\AppData\Local\pip\Cache - key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements-dev.txt') }} - restore-keys: | - ${{ runner.os }}-pip- - - # Set up Python + path: ${{ matrix.path }} + key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements*.txt') }} + restore-keys: ${{ runner.os }}-pip- - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v2 with: python-version: ${{ matrix.python-version }} - - # Install dependencies - name: Install dependencies run: | pip install -U pip setuptools wheel pip install -U tox tox-gh-actions - - # Run tests - name: Test with tox run: tox - - # Upload coverage report - name: Upload coverage to Codecov uses: codecov/codecov-action@v2 with: @@ -79,45 +53,29 @@ jobs: lint: runs-on: ubuntu-latest - steps: - # Check out latest code - uses: actions/checkout@v2 - - # Set up Python - name: Set up Python 3.10 uses: actions/setup-python@v2 with: python-version: "3.10" - - # Configure pip cache - name: Cache pip uses: actions/cache@v2 with: path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements-dev.txt') }} - restore-keys: | - ${{ runner.os }}-pip- - - # Configure pre-commit cache + key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements*.txt') }} + restore-keys: ${{ runner.os }}-pip- - name: Cache pre-commit uses: actions/cache@v2 with: path: ~/.cache/pre-commit key: ${{ runner.os }}-pre-commit-${{ hashFiles('**/.pre-commit-config.yaml') }} - restore-keys: | - ${{ runner.os }}-pre-commit- - - # Install dependencies + restore-keys: ${{ runner.os }}-pre-commit- - name: Install dependencies run: | pip install -U pip setuptools wheel pip install -U tox - - # Lint code - name: Lint code - run: tox -e lint - - # Lint docs + run: tox -e lint -- --show-diff-on-failure - name: Lint docs run: tox -e docs diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c65b2618..28b5f8a0 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,25 +2,27 @@ default_language_version: python: python3 repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.0.1 + rev: v4.1.0 hooks: - - id: trailing-whitespace - - id: end-of-file-fixer - - id: fix-encoding-pragma - args: [--remove] - - id: requirements-txt-fixer - id: check-ast - id: check-yaml - id: check-case-conflict - id: check-docstring-first - id: check-merge-conflict + - id: check-builtin-literals - id: debug-statements + - id: end-of-file-fixer + - id: fix-encoding-pragma + args: [--remove] + - id: requirements-txt-fixer + args: [requirements/requirements.txt, requirements/requirements-docs.txt, requirements/requirements-tests.txt] + - id: trailing-whitespace - repo: https://github.com/timothycrosley/isort - rev: 5.9.3 + rev: 5.10.1 hooks: - id: isort - repo: https://github.com/asottile/pyupgrade - rev: v2.29.0 + rev: v2.30.1 hooks: - id: pyupgrade args: [--py36-plus] @@ -29,12 +31,14 @@ repos: hooks: - id: python-no-eval - id: python-check-blanket-noqa + - id: python-check-mock-methods - id: python-use-type-annotations - id: rst-backticks - id: rst-directive-colons - id: rst-inline-touching-normal + - id: text-unicode-replacement-char - repo: https://github.com/psf/black - rev: 21.9b0 + rev: 21.12b0 hooks: - id: black args: [--safe, --quiet, --target-version=py36] @@ -44,7 +48,7 @@ repos: - id: flake8 additional_dependencies: [flake8-bugbear,flake8-annotations] - repo: https://github.com/pre-commit/mirrors-mypy - rev: 'v0.910-1' + rev: 'v0.930' hooks: - id: mypy additional_dependencies: [types-python-dateutil] diff --git a/Makefile b/Makefile index c473e3f1..5f885157 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ .PHONY: auto test docs clean -auto: build39 +auto: build310 build36: PYTHON_VER = python3.6 build37: PYTHON_VER = python3.7 @@ -12,7 +12,8 @@ build36 build37 build38 build39 build310: clean $(PYTHON_VER) -m venv venv . venv/bin/activate; \ pip install -U pip setuptools wheel; \ - pip install -r requirements-dev.txt; \ + pip install -r requirements/requirements-tests.txt; \ + pip install -r requirements/requirements-docs.txt; \ pre-commit install test: @@ -22,14 +23,20 @@ test: lint: . venv/bin/activate; \ - pre-commit run --all-files --show-diff-on-failure + pre-commit run --all-files -docs: +clean-docs: rm -rf docs/_build + +docs: . venv/bin/activate; \ cd docs; \ make html +live-docs: clean-docs + . venv/bin/activate; \ + sphinx-autobuild docs docs/_build/html + clean: clean-dist rm -rf venv .pytest_cache ./**/__pycache__ rm -f .coverage coverage.xml ./**/*.pyc diff --git a/arrow/locales.py b/arrow/locales.py index ddbecb77..d5044608 100644 --- a/arrow/locales.py +++ b/arrow/locales.py @@ -46,7 +46,7 @@ ] -_locale_map: Dict[str, Type["Locale"]] = dict() +_locale_map: Dict[str, Type["Locale"]] = {} def get_locale(name: str) -> "Locale": @@ -5743,7 +5743,7 @@ class SinhalaLocale(Locale): } # Sinhala: the general format to describe timeframe is different from past and future, # so we do not copy the original timeframes dictionary - timeframes_only_distance = dict() + timeframes_only_distance = {} timeframes_only_distance["second"] = "තත්පරයක්" timeframes_only_distance["seconds"] = "තත්පර {0}" timeframes_only_distance["minute"] = "මිනිත්තුවක්" diff --git a/requirements/requirements-docs.txt b/requirements/requirements-docs.txt new file mode 100644 index 00000000..de59f1a3 --- /dev/null +++ b/requirements/requirements-docs.txt @@ -0,0 +1,5 @@ +-r requirements.txt +doc8 +sphinx +sphinx-autobuild +sphinx-autodoc-typehints diff --git a/requirements-dev.txt b/requirements/requirements-tests.txt similarity index 51% rename from requirements-dev.txt rename to requirements/requirements-tests.txt index 75f44341..7e9fbe3f 100644 --- a/requirements-dev.txt +++ b/requirements/requirements-tests.txt @@ -1,11 +1,10 @@ +-r requirements.txt dateparser==1.* -pre-commit==2.* -pytest==6.* -pytest-cov==3.* -pytest-mock==3.* +pre-commit +pytest +pytest-cov +pytest-mock python-dateutil>=2.7.0 pytz==2021.1 simplejson==3.* -sphinx==4.* -sphinx-autodoc-typehints==1.* typing_extensions; python_version < '3.8' diff --git a/requirements/requirements.txt b/requirements/requirements.txt new file mode 100644 index 00000000..bcdff0e8 --- /dev/null +++ b/requirements/requirements.txt @@ -0,0 +1,2 @@ +python-dateutil>=2.7.0 +typing_extensions; python_version < '3.8' diff --git a/tox.ini b/tox.ini index b7746176..fefa3e7e 100644 --- a/tox.ini +++ b/tox.ini @@ -13,27 +13,22 @@ python = 3.10: py310 [testenv] -deps = -rrequirements-dev.txt +deps = -r requirements/requirements-tests.txt allowlist_externals = pytest commands = pytest [testenv:lint] -basepython = python3 skip_install = true deps = pre-commit -commands = - pre-commit install - pre-commit run --all-files --show-diff-on-failure +commands_pre = pre-commit install +commands = pre-commit run --all-files {posargs} [testenv:docs] -basepython = python3 skip_install = true changedir = docs deps = - doc8 - sphinx - sphinx-autodoc-typehints - python-dateutil + -r requirements/requirements-tests.txt + -r requirements/requirements-docs.txt allowlist_externals = make commands = doc8 index.rst ../README.rst --extension .rst --ignore D001 From 022845e639993dd9ccbcee01014ae7b5ea0671a6 Mon Sep 17 00:00:00 2001 From: Jad Chaar Date: Fri, 31 Dec 2021 18:28:43 -0500 Subject: [PATCH 03/44] Generate doc PDFs with xelatex, which has better support for unicode characters. --- docs/conf.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/conf.py b/docs/conf.py index 907d78c0..f106cb7f 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -58,3 +58,7 @@ html_sidebars = { "**": ["about.html", "localtoc.html", "relations.html", "searchbox.html"] } + +# Generate PDFs with unicode characters +# https://docs.readthedocs.io/en/stable/guides/pdf-non-ascii-languages.html +latex_engine = "xelatex" From fecbada0e4cc06b52f08a9e1151045b9e056ab3d Mon Sep 17 00:00:00 2001 From: Jad Chaar Date: Fri, 31 Dec 2021 22:05:28 -0500 Subject: [PATCH 04/44] Fix requirements files in MANIFEST.in --- MANIFEST.in | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MANIFEST.in b/MANIFEST.in index 8ac191e0..9abe9773 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,3 +1,4 @@ -include LICENSE CHANGELOG.rst README.rst Makefile requirements-dev.txt tox.ini +include LICENSE CHANGELOG.rst README.rst Makefile tox.ini +recursive-include requirements *.txt recursive-include tests *.py recursive-include docs *.py *.rst *.bat Makefile From 7d7926a056f820743352b9ffbb0043f49505ec2f Mon Sep 17 00:00:00 2001 From: kaiyang-code <57576013+kaiyang-code@users.noreply.github.com> Date: Sat, 1 Jan 2022 22:11:05 -0500 Subject: [PATCH 05/44] Expand Hong Kong, ChineseCN and ChineseTW Locale Test Cases (#1076) Co-authored-by: Anish Nyayachavadi --- tests/test_locales.py | 150 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 150 insertions(+) diff --git a/tests/test_locales.py b/tests/test_locales.py index 54536c0a..cb2f60ff 100644 --- a/tests/test_locales.py +++ b/tests/test_locales.py @@ -2018,6 +2018,56 @@ def test_format_timeframe(self): assert self.locale._format_timeframe("year", 1) == "1年" assert self.locale._format_timeframe("years", 12) == "12年" + assert self.locale._format_timeframe("second", -1) == "1秒" + assert self.locale._format_timeframe("seconds", -30) == "30秒" + assert self.locale._format_timeframe("minute", -1) == "1分鐘" + assert self.locale._format_timeframe("minutes", -40) == "40分鐘" + assert self.locale._format_timeframe("hour", -1) == "1小時" + assert self.locale._format_timeframe("hours", -23) == "23小時" + assert self.locale._format_timeframe("day", -1) == "1天" + assert self.locale._format_timeframe("days", -12) == "12天" + assert self.locale._format_timeframe("week", -1) == "1星期" + assert self.locale._format_timeframe("weeks", -38) == "38星期" + assert self.locale._format_timeframe("month", -1) == "1個月" + assert self.locale._format_timeframe("months", -11) == "11個月" + assert self.locale._format_timeframe("year", -1) == "1年" + assert self.locale._format_timeframe("years", -12) == "12年" + + def test_format_relative_now(self): + assert self.locale._format_relative("剛才", "now", 0) == "剛才" + + def test_format_relative_past(self): + assert self.locale._format_relative("1秒", "second", 1) == "1秒後" + assert self.locale._format_relative("2秒", "seconds", 2) == "2秒後" + assert self.locale._format_relative("1分鐘", "minute", 1) == "1分鐘後" + assert self.locale._format_relative("2分鐘", "minutes", 2) == "2分鐘後" + assert self.locale._format_relative("1小時", "hour", 1) == "1小時後" + assert self.locale._format_relative("2小時", "hours", 2) == "2小時後" + assert self.locale._format_relative("1天", "day", 1) == "1天後" + assert self.locale._format_relative("2天", "days", 2) == "2天後" + assert self.locale._format_relative("1星期", "week", 1) == "1星期後" + assert self.locale._format_relative("2星期", "weeks", 2) == "2星期後" + assert self.locale._format_relative("1個月", "month", 1) == "1個月後" + assert self.locale._format_relative("2個月", "months", 2) == "2個月後" + assert self.locale._format_relative("1年", "year", 1) == "1年後" + assert self.locale._format_relative("2年", "years", 2) == "2年後" + + def test_format_relative_future(self): + assert self.locale._format_relative("1秒", "second", -1) == "1秒前" + assert self.locale._format_relative("2秒", "seconds", -2) == "2秒前" + assert self.locale._format_relative("1分鐘", "minute", -1) == "1分鐘前" + assert self.locale._format_relative("2分鐘", "minutes", -2) == "2分鐘前" + assert self.locale._format_relative("1小時", "hour", -1) == "1小時前" + assert self.locale._format_relative("2小時", "hours", -2) == "2小時前" + assert self.locale._format_relative("1天", "day", -1) == "1天前" + assert self.locale._format_relative("2天", "days", -2) == "2天前" + assert self.locale._format_relative("1星期", "week", -1) == "1星期前" + assert self.locale._format_relative("2星期", "weeks", -2) == "2星期前" + assert self.locale._format_relative("1個月", "month", -1) == "1個月前" + assert self.locale._format_relative("2個月", "months", -2) == "2個月前" + assert self.locale._format_relative("1年", "year", -1) == "1年前" + assert self.locale._format_relative("2年", "years", -2) == "2年前" + @pytest.mark.usefixtures("lang_locale") class TestChineseTWLocale: @@ -2038,6 +2088,56 @@ def test_format_timeframe(self): assert self.locale._format_timeframe("year", 1) == "1年" assert self.locale._format_timeframe("years", 12) == "12年" + assert self.locale._format_timeframe("second", -1) == "1秒" + assert self.locale._format_timeframe("seconds", -30) == "30秒" + assert self.locale._format_timeframe("minute", -1) == "1分鐘" + assert self.locale._format_timeframe("minutes", -40) == "40分鐘" + assert self.locale._format_timeframe("hour", -1) == "1小時" + assert self.locale._format_timeframe("hours", -23) == "23小時" + assert self.locale._format_timeframe("day", -1) == "1天" + assert self.locale._format_timeframe("days", -12) == "12天" + assert self.locale._format_timeframe("week", -1) == "1週" + assert self.locale._format_timeframe("weeks", -38) == "38週" + assert self.locale._format_timeframe("month", -1) == "1個月" + assert self.locale._format_timeframe("months", -11) == "11個月" + assert self.locale._format_timeframe("year", -1) == "1年" + assert self.locale._format_timeframe("years", -12) == "12年" + + def test_format_relative_now(self): + assert self.locale._format_relative("剛才", "now", 0) == "剛才" + + def test_format_relative_past(self): + assert self.locale._format_relative("1秒", "second", 1) == "1秒後" + assert self.locale._format_relative("2秒", "seconds", 2) == "2秒後" + assert self.locale._format_relative("1分鐘", "minute", 1) == "1分鐘後" + assert self.locale._format_relative("2分鐘", "minutes", 2) == "2分鐘後" + assert self.locale._format_relative("1小時", "hour", 1) == "1小時後" + assert self.locale._format_relative("2小時", "hours", 2) == "2小時後" + assert self.locale._format_relative("1天", "day", 1) == "1天後" + assert self.locale._format_relative("2天", "days", 2) == "2天後" + assert self.locale._format_relative("1週", "week", 1) == "1週後" + assert self.locale._format_relative("2週", "weeks", 2) == "2週後" + assert self.locale._format_relative("1個月", "month", 1) == "1個月後" + assert self.locale._format_relative("2個月", "months", 2) == "2個月後" + assert self.locale._format_relative("1年", "year", 1) == "1年後" + assert self.locale._format_relative("2年", "years", 2) == "2年後" + + def test_format_relative_future(self): + assert self.locale._format_relative("1秒", "second", -1) == "1秒前" + assert self.locale._format_relative("2秒", "seconds", -2) == "2秒前" + assert self.locale._format_relative("1分鐘", "minute", -1) == "1分鐘前" + assert self.locale._format_relative("2分鐘", "minutes", -2) == "2分鐘前" + assert self.locale._format_relative("1小時", "hour", -1) == "1小時前" + assert self.locale._format_relative("2小時", "hours", -2) == "2小時前" + assert self.locale._format_relative("1天", "day", -1) == "1天前" + assert self.locale._format_relative("2天", "days", -2) == "2天前" + assert self.locale._format_relative("1週", "week", -1) == "1週前" + assert self.locale._format_relative("2週", "weeks", -2) == "2週前" + assert self.locale._format_relative("1個月", "month", -1) == "1個月前" + assert self.locale._format_relative("2個月", "months", -2) == "2個月前" + assert self.locale._format_relative("1年", "year", -1) == "1年前" + assert self.locale._format_relative("2年", "years", -2) == "2年前" + @pytest.mark.usefixtures("lang_locale") class TestChineseCNLocale: @@ -2058,6 +2158,56 @@ def test_format_timeframe(self): assert self.locale._format_timeframe("year", 1) == "1年" assert self.locale._format_timeframe("years", 12) == "12年" + assert self.locale._format_timeframe("second", -1) == "1秒" + assert self.locale._format_timeframe("seconds", -30) == "30秒" + assert self.locale._format_timeframe("minute", -1) == "1分钟" + assert self.locale._format_timeframe("minutes", -40) == "40分钟" + assert self.locale._format_timeframe("hour", -1) == "1小时" + assert self.locale._format_timeframe("hours", -23) == "23小时" + assert self.locale._format_timeframe("day", -1) == "1天" + assert self.locale._format_timeframe("days", -12) == "12天" + assert self.locale._format_timeframe("week", -1) == "1周" + assert self.locale._format_timeframe("weeks", -38) == "38周" + assert self.locale._format_timeframe("month", -1) == "1个月" + assert self.locale._format_timeframe("months", -11) == "11个月" + assert self.locale._format_timeframe("year", -1) == "1年" + assert self.locale._format_timeframe("years", -12) == "12年" + + def test_format_relative_now(self): + assert self.locale._format_relative("刚才", "now", 0) == "刚才" + + def test_format_relative_past(self): + assert self.locale._format_relative("1秒", "second", 1) == "1秒后" + assert self.locale._format_relative("2秒", "seconds", 2) == "2秒后" + assert self.locale._format_relative("1分钟", "minute", 1) == "1分钟后" + assert self.locale._format_relative("2分钟", "minutes", 2) == "2分钟后" + assert self.locale._format_relative("1小时", "hour", 1) == "1小时后" + assert self.locale._format_relative("2小时", "hours", 2) == "2小时后" + assert self.locale._format_relative("1天", "day", 1) == "1天后" + assert self.locale._format_relative("2天", "days", 2) == "2天后" + assert self.locale._format_relative("1周", "week", 1) == "1周后" + assert self.locale._format_relative("2周", "weeks", 2) == "2周后" + assert self.locale._format_relative("1个月", "month", 1) == "1个月后" + assert self.locale._format_relative("2个月", "months", 2) == "2个月后" + assert self.locale._format_relative("1年", "year", 1) == "1年后" + assert self.locale._format_relative("2年", "years", 2) == "2年后" + + def test_format_relative_future(self): + assert self.locale._format_relative("1秒", "second", -1) == "1秒前" + assert self.locale._format_relative("2秒", "seconds", -2) == "2秒前" + assert self.locale._format_relative("1分钟", "minute", -1) == "1分钟前" + assert self.locale._format_relative("2分钟", "minutes", -2) == "2分钟前" + assert self.locale._format_relative("1小时", "hour", -1) == "1小时前" + assert self.locale._format_relative("2小时", "hours", -2) == "2小时前" + assert self.locale._format_relative("1天", "day", -1) == "1天前" + assert self.locale._format_relative("2天", "days", -2) == "2天前" + assert self.locale._format_relative("1周", "week", -1) == "1周前" + assert self.locale._format_relative("2周", "weeks", -2) == "2周前" + assert self.locale._format_relative("1个月", "month", -1) == "1个月前" + assert self.locale._format_relative("2个月", "months", -2) == "2个月前" + assert self.locale._format_relative("1年", "year", -1) == "1年前" + assert self.locale._format_relative("2年", "years", -2) == "2年前" + @pytest.mark.usefixtures("lang_locale") class TestSwahiliLocale: From e43524088f78efacb425524445a886600660d854 Mon Sep 17 00:00:00 2001 From: Stian Jensen Date: Wed, 5 Jan 2022 22:29:06 +0100 Subject: [PATCH 06/44] Fix ordinals for norwegian languages (#1074) In Norwegian, there should be a period after the day of the month. This also adds basic test coverage to both norwegian languages, and fixes some small errors uncovered by those tests. --- arrow/locales.py | 12 ++++- tests/test_locales.py | 112 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 123 insertions(+), 1 deletion(-) diff --git a/arrow/locales.py b/arrow/locales.py index d5044608..b7fb0d32 100644 --- a/arrow/locales.py +++ b/arrow/locales.py @@ -2056,6 +2056,8 @@ class NorwegianLocale(Locale): "hours": "{0} timer", "day": "en dag", "days": "{0} dager", + "week": "en uke", + "weeks": "{0} uker", "month": "en måned", "months": "{0} måneder", "year": "ett år", @@ -2105,6 +2107,9 @@ class NorwegianLocale(Locale): ] day_abbreviations = ["", "ma", "ti", "on", "to", "fr", "lø", "sø"] + def _ordinal_number(self, n: int) -> str: + return f"{n}." + class NewNorwegianLocale(Locale): @@ -2123,7 +2128,9 @@ class NewNorwegianLocale(Locale): "hours": "{0} timar", "day": "ein dag", "days": "{0} dagar", - "month": "en månad", + "week": "ei veke", + "weeks": "{0} veker", + "month": "ein månad", "months": "{0} månader", "year": "eitt år", "years": "{0} år", @@ -2172,6 +2179,9 @@ class NewNorwegianLocale(Locale): ] day_abbreviations = ["", "må", "ty", "on", "to", "fr", "la", "su"] + def _ordinal_number(self, n: int) -> str: + return f"{n}." + class PortugueseLocale(Locale): names = ["pt", "pt-pt"] diff --git a/tests/test_locales.py b/tests/test_locales.py index cb2f60ff..5e879914 100644 --- a/tests/test_locales.py +++ b/tests/test_locales.py @@ -2702,3 +2702,115 @@ def test_plurals_mk(self): assert self.locale._format_timeframe("months", 11) == "11 ай" assert self.locale._format_timeframe("year", 1) == "бір жыл" assert self.locale._format_timeframe("years", 12) == "12 жыл" + + +@pytest.mark.usefixtures("lang_locale") +class TestNorwegianLocale: + def test_describe(self): + assert self.locale.describe("now", only_distance=True) == "nå nettopp" + assert self.locale.describe("now", only_distance=False) == "nå nettopp" + + def test_plurals(self): + assert self.locale._format_timeframe("now", 0) == "nå nettopp" + assert self.locale._format_timeframe("second", 1) == "ett sekund" + assert self.locale._format_timeframe("seconds", 30) == "30 sekunder" + assert self.locale._format_timeframe("minute", 1) == "ett minutt" + assert self.locale._format_timeframe("minutes", 40) == "40 minutter" + assert self.locale._format_timeframe("hour", 1) == "en time" + assert self.locale._format_timeframe("hours", 23) == "23 timer" + assert self.locale._format_timeframe("day", 1) == "en dag" + assert self.locale._format_timeframe("days", 12) == "12 dager" + assert self.locale._format_timeframe("week", 1) == "en uke" + assert self.locale._format_timeframe("weeks", 38) == "38 uker" + assert self.locale._format_timeframe("month", 1) == "en måned" + assert self.locale._format_timeframe("months", 11) == "11 måneder" + assert self.locale._format_timeframe("year", 1) == "ett år" + assert self.locale._format_timeframe("years", 12) == "12 år" + + def test_ordinal_number(self): + assert self.locale.ordinal_number(0) == "0." + assert self.locale.ordinal_number(1) == "1." + + def test_format_timeframe(self): + + assert self.locale._format_timeframe("hours", 2) == "2 timer" + assert self.locale._format_timeframe("hour", 0) == "en time" + + def test_format_relative_now(self): + + result = self.locale._format_relative("nå nettopp", "now", 0) + + assert result == "nå nettopp" + + def test_format_relative_past(self): + + result = self.locale._format_relative("en time", "hour", 1) + + assert result == "om en time" + + def test_format_relative_future(self): + + result = self.locale._format_relative("en time", "hour", -1) + + assert result == "for en time siden" + + def test_weekday(self): + dt = arrow.Arrow(2015, 4, 11, 17, 30, 00) + assert self.locale.day_name(dt.isoweekday()) == "lørdag" + assert self.locale.day_abbreviation(dt.isoweekday()) == "lø" + + +@pytest.mark.usefixtures("lang_locale") +class TestNewNorwegianLocale: + def test_describe(self): + assert self.locale.describe("now", only_distance=True) == "no nettopp" + assert self.locale.describe("now", only_distance=False) == "no nettopp" + + def test_plurals(self): + assert self.locale._format_timeframe("now", 0) == "no nettopp" + assert self.locale._format_timeframe("second", 1) == "eitt sekund" + assert self.locale._format_timeframe("seconds", 30) == "30 sekund" + assert self.locale._format_timeframe("minute", 1) == "eitt minutt" + assert self.locale._format_timeframe("minutes", 40) == "40 minutt" + assert self.locale._format_timeframe("hour", 1) == "ein time" + assert self.locale._format_timeframe("hours", 23) == "23 timar" + assert self.locale._format_timeframe("day", 1) == "ein dag" + assert self.locale._format_timeframe("days", 12) == "12 dagar" + assert self.locale._format_timeframe("week", 1) == "ei veke" + assert self.locale._format_timeframe("weeks", 38) == "38 veker" + assert self.locale._format_timeframe("month", 1) == "ein månad" + assert self.locale._format_timeframe("months", 11) == "11 månader" + assert self.locale._format_timeframe("year", 1) == "eitt år" + assert self.locale._format_timeframe("years", 12) == "12 år" + + def test_ordinal_number(self): + assert self.locale.ordinal_number(0) == "0." + assert self.locale.ordinal_number(1) == "1." + + def test_format_timeframe(self): + + assert self.locale._format_timeframe("hours", 2) == "2 timar" + assert self.locale._format_timeframe("hour", 0) == "ein time" + + def test_format_relative_now(self): + + result = self.locale._format_relative("no nettopp", "now", 0) + + assert result == "no nettopp" + + def test_format_relative_past(self): + + result = self.locale._format_relative("ein time", "hour", 1) + + assert result == "om ein time" + + def test_format_relative_future(self): + + result = self.locale._format_relative("ein time", "hour", -1) + + assert result == "for ein time sidan" + + def test_weekday(self): + dt = arrow.Arrow(2015, 4, 11, 17, 30, 00) + assert self.locale.day_name(dt.isoweekday()) == "laurdag" + assert self.locale.day_abbreviation(dt.isoweekday()) == "la" From 4c4689c6d97ed2b1f37a67b96c561266c66ee088 Mon Sep 17 00:00:00 2001 From: Gaganpreet Date: Wed, 19 Jan 2022 22:00:01 +0100 Subject: [PATCH 07/44] Fix outdated Python 2 print in docs (#1086) --- arrow/arrow.py | 2 +- docs/index.rst | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/arrow/arrow.py b/arrow/arrow.py index d7504456..21b0347f 100644 --- a/arrow/arrow.py +++ b/arrow/arrow.py @@ -759,7 +759,7 @@ def interval( >>> start = datetime(2013, 5, 5, 12, 30) >>> end = datetime(2013, 5, 5, 17, 15) >>> for r in arrow.Arrow.interval('hour', start, end, 2): - ... print r + ... print(r) ... (, ) (, ) diff --git a/docs/index.rst b/docs/index.rst index 43895b04..d4f9ec2a 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -315,7 +315,7 @@ You can also get a range of time spans: >>> start = datetime(2013, 5, 5, 12, 30) >>> end = datetime(2013, 5, 5, 17, 15) >>> for r in arrow.Arrow.span_range('hour', start, end): - ... print r + ... print(r) ... (, ) (, ) @@ -330,7 +330,7 @@ Or just iterate over a range of time: >>> start = datetime(2013, 5, 5, 12, 30) >>> end = datetime(2013, 5, 5, 17, 15) >>> for r in arrow.Arrow.range('hour', start, end): - ... print repr(r) + ... print(repr(r)) ... From f8a65212df1ab234013a8a116436b3532c82b05d Mon Sep 17 00:00:00 2001 From: Chris <30196510+systemcatch@users.noreply.github.com> Date: Thu, 20 Jan 2022 13:20:21 +0000 Subject: [PATCH 08/44] Bump version to 1.2.2 and update CHANGELOG (#1085) --- CHANGELOG.rst | 11 +++++++++++ arrow/_version.py | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 87a1ef2b..12bc86a6 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,17 @@ Changelog ========= +1.2.2 (2022-01-19) +------------------ + +- [NEW] Added Kazakh locale. +- [FIX] The Belarusian, Bulgarian, Czech, Macedonian, Polish, Russian, Slovak and Ukrainian locales now support ``dehumanize``. +- [FIX] Minor bug fixes and improvements to ChineseCN, Indonesian, Norwegian, and Russian locales. +- [FIX] Expanded testing for multiple locales. +- [INTERNAL] Started using ``xelatex`` for pdf generation in documentation. +- [INTERNAL] Split requirements file into ``requirements.txt``, ``requirements-docs.txt`` and ``requirements-tests.txt``. +- [INTERNAL] Added ``flake8-annotations`` package for type linting in ``pre-commit``. + 1.2.1 (2021-10-24) ------------------ diff --git a/arrow/_version.py b/arrow/_version.py index a955fdae..bc86c944 100644 --- a/arrow/_version.py +++ b/arrow/_version.py @@ -1 +1 @@ -__version__ = "1.2.1" +__version__ = "1.2.2" From cdc8d337835ab4132e0a3f3de1f73581eaa42e81 Mon Sep 17 00:00:00 2001 From: fjerhammer Date: Mon, 14 Feb 2022 05:04:52 +0100 Subject: [PATCH 09/44] Fix Issues and Add Tests for Danish Language (#1091) --- arrow/locales.py | 9 ++++++-- tests/test_locales.py | 52 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 2 deletions(-) diff --git a/arrow/locales.py b/arrow/locales.py index b7fb0d32..3c1a3bb9 100644 --- a/arrow/locales.py +++ b/arrow/locales.py @@ -2866,19 +2866,21 @@ class DanishLocale(Locale): names = ["da", "da-dk"] past = "for {0} siden" - future = "efter {0}" + future = "om {0}" and_word = "og" timeframes = { "now": "lige nu", "second": "et sekund", - "seconds": "{0} et par sekunder", + "seconds": "{0} sekunder", "minute": "et minut", "minutes": "{0} minutter", "hour": "en time", "hours": "{0} timer", "day": "en dag", "days": "{0} dage", + "week": "en uge", + "weeks": "{0} uger", "month": "en måned", "months": "{0} måneder", "year": "et år", @@ -2928,6 +2930,9 @@ class DanishLocale(Locale): ] day_abbreviations = ["", "man", "tir", "ons", "tor", "fre", "lør", "søn"] + def _ordinal_number(self, n: int) -> str: + return f"{n}." + class MalayalamLocale(Locale): diff --git a/tests/test_locales.py b/tests/test_locales.py index 5e879914..a9da0bc0 100644 --- a/tests/test_locales.py +++ b/tests/test_locales.py @@ -2814,3 +2814,55 @@ def test_weekday(self): dt = arrow.Arrow(2015, 4, 11, 17, 30, 00) assert self.locale.day_name(dt.isoweekday()) == "laurdag" assert self.locale.day_abbreviation(dt.isoweekday()) == "la" + + +@pytest.mark.usefixtures("lang_locale") +class TestDanishLocale: + def test_describe(self): + assert self.locale.describe("now", only_distance=True) == "lige nu" + assert self.locale.describe("now", only_distance=False) == "lige nu" + + def test_plurals(self): + assert self.locale._format_timeframe("now", 0) == "lige nu" + assert self.locale._format_timeframe("second", 1) == "et sekund" + assert self.locale._format_timeframe("seconds", 30) == "30 sekunder" + assert self.locale._format_timeframe("minute", 1) == "et minut" + assert self.locale._format_timeframe("minutes", 40) == "40 minutter" + assert self.locale._format_timeframe("hour", 1) == "en time" + assert self.locale._format_timeframe("hours", 23) == "23 timer" + assert self.locale._format_timeframe("day", 1) == "en dag" + assert self.locale._format_timeframe("days", 12) == "12 dage" + assert self.locale._format_timeframe("week", 1) == "en uge" + assert self.locale._format_timeframe("weeks", 38) == "38 uger" + assert self.locale._format_timeframe("month", 1) == "en måned" + assert self.locale._format_timeframe("months", 11) == "11 måneder" + assert self.locale._format_timeframe("year", 1) == "et år" + assert self.locale._format_timeframe("years", 12) == "12 år" + + def test_ordinal_number(self): + assert self.locale.ordinal_number(0) == "0." + assert self.locale.ordinal_number(1) == "1." + + def test_format_timeframe(self): + assert self.locale._format_timeframe("hours", 2) == "2 timer" + assert self.locale._format_timeframe("hour", 0) == "en time" + + def test_format_relative_now(self): + result = self.locale._format_relative("lige nu", "now", 0) + + assert result == "lige nu" + + def test_format_relative_past(self): + result = self.locale._format_relative("en time", "hour", 1) + + assert result == "om en time" + + def test_format_relative_future(self): + result = self.locale._format_relative("en time", "hour", -1) + + assert result == "for en time siden" + + def test_weekday(self): + dt = arrow.Arrow(2015, 4, 11, 17, 30, 00) + assert self.locale.day_name(dt.isoweekday()) == "lørdag" + assert self.locale.day_abbreviation(dt.isoweekday()) == "lør" From 5ad47f1cf58fe4b2f053e37be04362096441716b Mon Sep 17 00:00:00 2001 From: cyriaka90 Date: Mon, 14 Feb 2022 14:20:49 +0100 Subject: [PATCH 10/44] Add Georgian Locale (#1088) --- arrow/constants.py | 2 + arrow/locales.py | 85 +++++++++++++++++++++++++++++++++++++++++++ tests/test_arrow.py | 2 + tests/test_locales.py | 53 +++++++++++++++++++++++++++ 4 files changed, 142 insertions(+) diff --git a/arrow/constants.py b/arrow/constants.py index 1189d07c..92665043 100644 --- a/arrow/constants.py +++ b/arrow/constants.py @@ -162,6 +162,8 @@ "ta-lk", "ur", "ur-pk", + "ka", + "ka-ge", "kk", "kk-kz", } diff --git a/arrow/locales.py b/arrow/locales.py index 3c1a3bb9..4164ebdf 100644 --- a/arrow/locales.py +++ b/arrow/locales.py @@ -5704,6 +5704,91 @@ class AlbanianLocale(Locale): ] +class GeorgianLocale(Locale): + + names = ["ka", "ka-ge"] + + past = "{0} წინ" # ts’in + future = "{0} შემდეგ" # shemdeg + and_word = "და" # da + + timeframes = { + "now": "ახლა", # akhla + # When a cardinal qualifies a noun, it stands in the singular + "second": "წამის", # ts’amis + "seconds": "{0} წამის", + "minute": "წუთის", # ts’utis + "minutes": "{0} წუთის", + "hour": "საათის", # saatis + "hours": "{0} საათის", + "day": "დღის", # dghis + "days": "{0} დღის", + "week": "კვირის", # k’viris + "weeks": "{0} კვირის", + "month": "თვის", # tvis + "months": "{0} თვის", + "year": "წლის", # ts’lis + "years": "{0} წლის", + } + + month_names = [ + # modern month names + "", + "იანვარი", # Ianvari + "თებერვალი", # Tebervali + "მარტი", # Mart'i + "აპრილი", # Ap'rili + "მაისი", # Maisi + "ივნისი", # Ivnisi + "ივლისი", # Ivlisi + "აგვისტო", # Agvist'o + "სექტემბერი", # Sekt'emberi + "ოქტომბერი", # Okt'omberi + "ნოემბერი", # Noemberi + "დეკემბერი", # Dek'emberi + ] + + month_abbreviations = [ + # no abbr. found yet + "", + "იანვარი", # Ianvari + "თებერვალი", # Tebervali + "მარტი", # Mart'i + "აპრილი", # Ap'rili + "მაისი", # Maisi + "ივნისი", # Ivnisi + "ივლისი", # Ivlisi + "აგვისტო", # Agvist'o + "სექტემბერი", # Sekt'emberi + "ოქტომბერი", # Okt'omberi + "ნოემბერი", # Noemberi + "დეკემბერი", # Dek'emberi + ] + + day_names = [ + "", + "ორშაბათი", # orshabati + "სამშაბათი", # samshabati + "ოთხშაბათი", # otkhshabati + "ხუთშაბათი", # khutshabati + "პარასკევი", # p’arask’evi + "შაბათი", # shabati + # "k’vira" also serves as week; to avoid confusion "k’vira-dge" can be used for Sunday + "კვირა", # k’vira + ] + + day_abbreviations = [ + "", + "ორშაბათი", # orshabati + "სამშაბათი", # samshabati + "ოთხშაბათი", # otkhshabati + "ხუთშაბათი", # khutshabati + "პარასკევი", # p’arask’evi + "შაბათი", # shabati + "კვირა", # k’vira + ] + + class SinhalaLocale(Locale): names = ["si", "si-lk"] diff --git a/tests/test_arrow.py b/tests/test_arrow.py index 60f3a64b..2e2ffe91 100644 --- a/tests/test_arrow.py +++ b/tests/test_arrow.py @@ -2466,6 +2466,8 @@ def locale_list_no_weeks() -> List[str]: "ta-lk", "ur", "ur-pk", + "ka", + "ka-ge", "kk", "kk-kz", ] diff --git a/tests/test_locales.py b/tests/test_locales.py index a9da0bc0..ba2300bd 100644 --- a/tests/test_locales.py +++ b/tests/test_locales.py @@ -1161,6 +1161,59 @@ def test_ordinal_number(self): assert self.locale.ordinal_number(1) == "1." +@pytest.mark.usefixtures("lang_locale") +class TestGeorgianLocale: + def test_format_timeframe(self): + # Now + assert self.locale._format_timeframe("now", 0) == "ახლა" + + # Second(s) + assert self.locale._format_timeframe("second", -1) == "წამის" + assert self.locale._format_timeframe("second", 1) == "წამის" + assert self.locale._format_timeframe("seconds", -3) == "3 წამის" + assert self.locale._format_timeframe("seconds", 3) == "3 წამის" + + # Minute(s) + assert self.locale._format_timeframe("minute", -1) == "წუთის" + assert self.locale._format_timeframe("minute", 1) == "წუთის" + assert self.locale._format_timeframe("minutes", -4) == "4 წუთის" + assert self.locale._format_timeframe("minutes", 4) == "4 წუთის" + + # Hour(s) + assert self.locale._format_timeframe("hour", -1) == "საათის" + assert self.locale._format_timeframe("hour", 1) == "საათის" + assert self.locale._format_timeframe("hours", -23) == "23 საათის" + assert self.locale._format_timeframe("hours", 23) == "23 საათის" + + # Day(s) + assert self.locale._format_timeframe("day", -1) == "დღის" + assert self.locale._format_timeframe("day", 1) == "დღის" + assert self.locale._format_timeframe("days", -12) == "12 დღის" + assert self.locale._format_timeframe("days", 12) == "12 დღის" + + # Day(s) + assert self.locale._format_timeframe("week", -1) == "კვირის" + assert self.locale._format_timeframe("week", 1) == "კვირის" + assert self.locale._format_timeframe("weeks", -12) == "12 კვირის" + assert self.locale._format_timeframe("weeks", 12) == "12 კვირის" + + # Month(s) + assert self.locale._format_timeframe("month", -1) == "თვის" + assert self.locale._format_timeframe("month", 1) == "თვის" + assert self.locale._format_timeframe("months", -2) == "2 თვის" + assert self.locale._format_timeframe("months", 2) == "2 თვის" + + # Year(s) + assert self.locale._format_timeframe("year", -1) == "წლის" + assert self.locale._format_timeframe("year", 1) == "წლის" + assert self.locale._format_timeframe("years", -2) == "2 წლის" + assert self.locale._format_timeframe("years", 2) == "2 წლის" + + def test_weekday(self): + dt = arrow.Arrow(2015, 4, 11, 17, 30, 00) + assert self.locale.day_name(dt.isoweekday()) == "შაბათი" + + @pytest.mark.usefixtures("lang_locale") class TestGermanLocale: def test_ordinal_number(self): From c9cae9e8c25f021bc1b0075ad7eba25d10463308 Mon Sep 17 00:00:00 2001 From: Ching-Yi <82449718+ChingYi-AX@users.noreply.github.com> Date: Fri, 18 Feb 2022 06:13:46 +0100 Subject: [PATCH 11/44] Add Amharic Locale (#1093) --- arrow/constants.py | 2 + arrow/locales.py | 171 ++++++++++++++++++++++++++++++++++++++++++ tests/test_locales.py | 76 +++++++++++++++++++ 3 files changed, 249 insertions(+) diff --git a/arrow/constants.py b/arrow/constants.py index 92665043..4c6fa5cb 100644 --- a/arrow/constants.py +++ b/arrow/constants.py @@ -166,4 +166,6 @@ "ka-ge", "kk", "kk-kz", + "am", + "am-et", } diff --git a/arrow/locales.py b/arrow/locales.py index 4164ebdf..d5652370 100644 --- a/arrow/locales.py +++ b/arrow/locales.py @@ -6100,3 +6100,174 @@ class KazakhLocale(Locale): "Жексенбі", ] day_abbreviations = ["", "Дс", "Сс", "Ср", "Бс", "Жм", "Сб", "Жс"] + + +class AmharicLocale(Locale): + names = ["am", "am-et"] + + past = "{0} በፊት" + future = "{0} ውስጥ" + and_word = "እና" + + timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[Mapping[str, str], str]]] = { + "now": "አሁን", + "second": { + "past": "ከአንድ ሰከንድ", + "future": "በአንድ ሰከንድ", + }, + "seconds": { + "past": "ከ {0} ሰከንድ", + "future": "በ {0} ሰከንድ", + }, + "minute": { + "past": "ከአንድ ደቂቃ", + "future": "በአንድ ደቂቃ", + }, + "minutes": { + "past": "ከ {0} ደቂቃዎች", + "future": "በ {0} ደቂቃዎች", + }, + "hour": { + "past": "ከአንድ ሰዓት", + "future": "በአንድ ሰዓት", + }, + "hours": { + "past": "ከ {0} ሰዓታት", + "future": "በ {0} ሰከንድ", + }, + "day": { + "past": "ከአንድ ቀን", + "future": "በአንድ ቀን", + }, + "days": { + "past": "ከ {0} ቀናት", + "future": "በ {0} ቀናት", + }, + "week": { + "past": "ከአንድ ሳምንት", + "future": "በአንድ ሳምንት", + }, + "weeks": { + "past": "ከ {0} ሳምንታት", + "future": "በ {0} ሳምንታት", + }, + "month": { + "past": "ከአንድ ወር", + "future": "በአንድ ወር", + }, + "months": { + "past": "ከ {0} ወር", + "future": "በ {0} ወራት", + }, + "year": { + "past": "ከአንድ አመት", + "future": "በአንድ አመት", + }, + "years": { + "past": "ከ {0} ዓመታት", + "future": "በ {0} ዓመታት", + }, + } + # Amharic: the general format to describe timeframe is different from past and future, + # so we do not copy the original timeframes dictionary + timeframes_only_distance = { + "second": "አንድ ሰከንድ", + "seconds": "{0} ሰከንድ", + "minute": "አንድ ደቂቃ", + "minutes": "{0} ደቂቃዎች", + "hour": "አንድ ሰዓት", + "hours": "{0} ሰዓት", + "day": "አንድ ቀን", + "days": "{0} ቀናት", + "week": "አንድ ሳምንት", + "weeks": "{0} ሳምንት", + "month": "አንድ ወር", + "months": "{0} ወራት", + "year": "አንድ አመት", + "years": "{0} ዓመታት", + } + + month_names = [ + "", + "ጃንዩወሪ", + "ፌብሩወሪ", + "ማርች", + "ኤፕሪል", + "ሜይ", + "ጁን", + "ጁላይ", + "ኦገስት", + "ሴፕቴምበር", + "ኦክቶበር", + "ኖቬምበር", + "ዲሴምበር", + ] + + month_abbreviations = [ + "", + "ጃንዩ", + "ፌብሩ", + "ማርች", + "ኤፕሪ", + "ሜይ", + "ጁን", + "ጁላይ", + "ኦገስ", + "ሴፕቴ", + "ኦክቶ", + "ኖቬም", + "ዲሴም", + ] + + day_names = [ + "", + "ሰኞ", + "ማክሰኞ", + "ረቡዕ", + "ሐሙስ", + "ዓርብ", + "ቅዳሜ", + "እሑድ", + ] + day_abbreviations = ["", "እ", "ሰ", "ማ", "ረ", "ሐ", "ዓ", "ቅ"] + + def _ordinal_number(self, n: int) -> str: + return f"{n}ኛ" + + def _format_timeframe(self, timeframe: TimeFrameLiteral, delta: int) -> str: + """ + Amharic awares time frame format function, takes into account + the differences between general, past, and future forms (three different suffixes). + """ + abs_delta = abs(delta) + form = self.timeframes[timeframe] + + if isinstance(form, str): + return form.format(abs_delta) + + if delta > 0: + key = "future" + else: + key = "past" + form = form[key] + + return form.format(abs_delta) + + def describe( + self, + timeframe: TimeFrameLiteral, + delta: Union[float, int] = 1, # key is always future when only_distance=False + only_distance: bool = False, + ) -> str: + """Describes a delta within a timeframe in plain language. + + :param timeframe: a string representing a timeframe. + :param delta: a quantity representing a delta in a timeframe. + :param only_distance: return only distance eg: "11 seconds" without "in" or "ago" keywords + """ + + if not only_distance: + return super().describe(timeframe, delta, only_distance) + humanized = self.timeframes_only_distance[timeframe].format(trunc(abs(delta))) + + return humanized diff --git a/tests/test_locales.py b/tests/test_locales.py index ba2300bd..0e42074d 100644 --- a/tests/test_locales.py +++ b/tests/test_locales.py @@ -2919,3 +2919,79 @@ def test_weekday(self): dt = arrow.Arrow(2015, 4, 11, 17, 30, 00) assert self.locale.day_name(dt.isoweekday()) == "lørdag" assert self.locale.day_abbreviation(dt.isoweekday()) == "lør" + + +@pytest.mark.usefixtures("lang_locale") +class TestAmharicLocale: + def test_format_timeframe(self): + assert self.locale._format_timeframe("now", 0) == "አሁን" + # second(s) + assert self.locale._format_timeframe("second", 1) == "በአንድ ሰከንድ" + assert self.locale._format_timeframe("second", -1) == "ከአንድ ሰከንድ" + assert self.locale._format_timeframe("seconds", 6) == "በ 6 ሰከንድ" + assert self.locale._format_timeframe("seconds", -36) == "ከ 36 ሰከንድ" + # minute(s) + assert self.locale._format_timeframe("minute", 1) == "በአንድ ደቂቃ" + assert self.locale._format_timeframe("minute", -1) == "ከአንድ ደቂቃ" + assert self.locale._format_timeframe("minutes", 7) == "በ 7 ደቂቃዎች" + assert self.locale._format_timeframe("minutes", -20) == "ከ 20 ደቂቃዎች" + # hour(s) + assert self.locale._format_timeframe("hour", 1) == "በአንድ ሰዓት" + assert self.locale._format_timeframe("hour", -1) == "ከአንድ ሰዓት" + assert self.locale._format_timeframe("hours", 7) == "በ 7 ሰከንድ" + assert self.locale._format_timeframe("hours", -20) == "ከ 20 ሰዓታት" + # day(s) + assert self.locale._format_timeframe("day", 1) == "በአንድ ቀን" + assert self.locale._format_timeframe("day", -1) == "ከአንድ ቀን" + assert self.locale._format_timeframe("days", 7) == "በ 7 ቀናት" + assert self.locale._format_timeframe("days", -20) == "ከ 20 ቀናት" + # week(s) + assert self.locale._format_timeframe("week", 1) == "በአንድ ሳምንት" + assert self.locale._format_timeframe("week", -1) == "ከአንድ ሳምንት" + assert self.locale._format_timeframe("weeks", 7) == "በ 7 ሳምንታት" + assert self.locale._format_timeframe("weeks", -20) == "ከ 20 ሳምንታት" + # month(s) + assert self.locale._format_timeframe("month", 1) == "በአንድ ወር" + assert self.locale._format_timeframe("month", -1) == "ከአንድ ወር" + assert self.locale._format_timeframe("months", 7) == "በ 7 ወራት" + assert self.locale._format_timeframe("months", -20) == "ከ 20 ወር" + # year(s) + assert self.locale._format_timeframe("year", 1) == "በአንድ አመት" + assert self.locale._format_timeframe("year", -1) == "ከአንድ አመት" + assert self.locale._format_timeframe("years", 7) == "በ 7 ዓመታት" + assert self.locale._format_timeframe("years", -20) == "ከ 20 ዓመታት" + + def test_describe_am(self): + assert self.locale.describe("second", only_distance=True) == "አንድ ሰከንድ" + assert ( + self.locale.describe("second", only_distance=False) == "በአንድ ሰከንድ ውስጥ" + ) # (in) a second + + assert self.locale.describe("minute", only_distance=True) == "አንድ ደቂቃ" + assert ( + self.locale.describe("minute", only_distance=False) == "በአንድ ደቂቃ ውስጥ" + ) # (in) a minute + + def test_format_relative_now(self): + result = self.locale._format_relative("አሁን", "now", 0) + assert result == "አሁን" + + def test_ordinal_number(self): + assert self.locale.ordinal_number(1) == "1ኛ" + + def test_format_relative_future(self): + + result = self.locale._format_relative("በአንድ ሰዓት", "hour", 1) + + assert result == "በአንድ ሰዓት ውስጥ" # (in) one hour + + def test_format_relative_past(self): + + result = self.locale._format_relative("ከአንድ ሰዓት", "hour", -1) + + assert result == "ከአንድ ሰዓት በፊት" # an hour ago + + def test_weekday(self): + dt = arrow.Arrow(2015, 4, 11, 17, 30, 00) + assert self.locale.day_name(dt.isoweekday()) == "ቅዳሜ" + assert self.locale.day_abbreviation(dt.isoweekday()) == "ዓ" From 80af201e58221e2106eb4264cf45c16282ed5270 Mon Sep 17 00:00:00 2001 From: Elaheh Salehi Rizi Date: Wed, 2 Mar 2022 14:39:26 +0100 Subject: [PATCH 12/44] Add Armenian locale --- arrow/constants.py | 2 + arrow/locales.py | 86 +++++++++++++++++++++++++++++++++++++++++++ tests/test_arrow.py | 4 ++ tests/test_locales.py | 64 ++++++++++++++++++++++++++++++++ 4 files changed, 156 insertions(+) diff --git a/arrow/constants.py b/arrow/constants.py index 4c6fa5cb..83627cd3 100644 --- a/arrow/constants.py +++ b/arrow/constants.py @@ -168,4 +168,6 @@ "kk-kz", "am", "am-et", + "hy-am", + "hy", } diff --git a/arrow/locales.py b/arrow/locales.py index d5652370..37791c8b 100644 --- a/arrow/locales.py +++ b/arrow/locales.py @@ -6271,3 +6271,89 @@ def describe( humanized = self.timeframes_only_distance[timeframe].format(trunc(abs(delta))) return humanized + + +class ArmenianLocale(Locale): + names = ["hy", "hy-am"] + past = "{0} առաջ" + future = "{0}ից" + and_word = "Եվ" + + timeframes = { + "now": "հիմա", + "second": "վայրկյան", + "seconds": "{0} վայրկյան", + "minute": "րոպե", + "minutes": "{0} րոպե", + "hour": "ժամ", + "hours": "{0} ժամ", + "day": "օր", + "days": "{0} օր", + "month": "ամիս", + "months": "{0} ամիս", + "year": "տարին", + "years": "{0} տարին", + "week": "շաբաթ", + "weeks": "{0} շաբաթ", + } + + meridians = { + "am": "Ամ", + "pm": "պ.մ.", + "AM": "Ամ", + "PM": "պ.մ.", + } + + month_names = [ + "", + "հունվար", + "փետրվար", + "մարտ", + "ապրիլ", + "մայիս", + "հունիս", + "հուլիս", + "օգոստոս", + "սեպտեմբեր", + "հոկտեմբեր", + "նոյեմբեր", + "դեկտեմբեր", + ] + + month_abbreviations = [ + "", + "հունվար", + "փետրվար", + "մարտ", + "ապրիլ", + "մայիս", + "հունիս", + "հուլիս", + "օգոստոս", + "սեպտեմբեր", + "հոկտեմբեր", + "նոյեմբեր", + "դեկտեմբեր", + ] + + day_names = [ + "", + "երկուշաբթի", + "երեքշաբթի", + "չորեքշաբթի", + "հինգշաբթի", + "ուրբաթ", + "շաբաթ", + "կիրակի", + ] + + day_abbreviations = [ + "", + "երկ.", + "երեք.", + "չորեք.", + "հինգ.", + "ուրբ.", + "շաբ.", + "կիր.", + ] diff --git a/tests/test_arrow.py b/tests/test_arrow.py index 2e2ffe91..742f41d4 100644 --- a/tests/test_arrow.py +++ b/tests/test_arrow.py @@ -2470,6 +2470,8 @@ def locale_list_no_weeks() -> List[str]: "ka-ge", "kk", "kk-kz", + "hy", + "hy-am", ] return tested_langs @@ -2544,6 +2546,8 @@ def locale_list_with_weeks() -> List[str]: "ta-lk", "kk", "kk-kz", + "hy", + "hy-am", ] return tested_langs diff --git a/tests/test_locales.py b/tests/test_locales.py index 0e42074d..3ad3a2a7 100644 --- a/tests/test_locales.py +++ b/tests/test_locales.py @@ -2995,3 +2995,67 @@ def test_weekday(self): dt = arrow.Arrow(2015, 4, 11, 17, 30, 00) assert self.locale.day_name(dt.isoweekday()) == "ቅዳሜ" assert self.locale.day_abbreviation(dt.isoweekday()) == "ዓ" + + +@pytest.mark.usefixtures("lang_locale") +class TestArmenianLocale: + def test_describe(self): + assert self.locale.describe("now", only_distance=True) == "հիմա" + assert self.locale.describe("now", only_distance=False) == "հիմա" + + def test_plurals(self): + assert self.locale._format_timeframe("now", 0) == "հիմա" + assert self.locale._format_timeframe("second", 1) == "վայրկյան" + assert self.locale._format_timeframe("seconds", 30) == "30 վայրկյան" + assert self.locale._format_timeframe("minute", 1) == "րոպե" + assert self.locale._format_timeframe("minutes", 40) == "40 րոպե" + assert self.locale._format_timeframe("hour", 1) == "ժամ" + assert self.locale._format_timeframe("hours", 23) == "23 ժամ" + assert self.locale._format_timeframe("day", 1) == "օր" + assert self.locale._format_timeframe("days", 12) == "12 օր" + assert self.locale._format_timeframe("month", 1) == "ամիս" + assert self.locale._format_timeframe("months", 11) == "11 ամիս" + assert self.locale._format_timeframe("year", 1) == "տարին" + assert self.locale._format_timeframe("years", 12) == "12 տարին" + + def test_format_timeframe(self): + # Second(s) + assert self.locale._format_timeframe("second", -1) == "վայրկյան" + assert self.locale._format_timeframe("second", 1) == "վայրկյան" + assert self.locale._format_timeframe("seconds", -3) == "3 վայրկյան" + assert self.locale._format_timeframe("seconds", 3) == "3 վայրկյան" + + # Minute(s) + assert self.locale._format_timeframe("minute", -1) == "րոպե" + assert self.locale._format_timeframe("minute", 1) == "րոպե" + assert self.locale._format_timeframe("minutes", -4) == "4 րոպե" + assert self.locale._format_timeframe("minutes", 4) == "4 րոպե" + + # Hour(s) + assert self.locale._format_timeframe("hour", -1) == "ժամ" + assert self.locale._format_timeframe("hour", 1) == "ժամ" + assert self.locale._format_timeframe("hours", -23) == "23 ժամ" + assert self.locale._format_timeframe("hours", 23) == "23 ժամ" + + # Day(s) + assert self.locale._format_timeframe("day", -1) == "օր" + assert self.locale._format_timeframe("day", 1) == "օր" + assert self.locale._format_timeframe("days", -12) == "12 օր" + assert self.locale._format_timeframe("days", 12) == "12 օր" + + # Month(s) + assert self.locale._format_timeframe("month", -1) == "ամիս" + assert self.locale._format_timeframe("month", 1) == "ամիս" + assert self.locale._format_timeframe("months", -2) == "2 ամիս" + assert self.locale._format_timeframe("months", 2) == "2 ամիս" + + # Year(s) + assert self.locale._format_timeframe("year", -1) == "տարին" + assert self.locale._format_timeframe("year", 1) == "տարին" + assert self.locale._format_timeframe("years", -2) == "2 տարին" + assert self.locale._format_timeframe("years", 2) == "2 տարին" + + def test_weekday(self): + dt = arrow.Arrow(2015, 4, 11, 17, 30, 00) + assert self.locale.day_name(dt.isoweekday()) == "շաբաթ" + assert self.locale.day_abbreviation(dt.isoweekday()) == "շաբ." From ec98f1b064cf8fde9aae88d2499f733be6443775 Mon Sep 17 00:00:00 2001 From: Elaheh Salehi Rizi Date: Thu, 17 Mar 2022 09:34:37 +0100 Subject: [PATCH 13/44] Add Armenian --- tests/test_locales.py | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/tests/test_locales.py b/tests/test_locales.py index 3ad3a2a7..98f55ddd 100644 --- a/tests/test_locales.py +++ b/tests/test_locales.py @@ -3003,20 +3003,11 @@ def test_describe(self): assert self.locale.describe("now", only_distance=True) == "հիմա" assert self.locale.describe("now", only_distance=False) == "հիմա" - def test_plurals(self): - assert self.locale._format_timeframe("now", 0) == "հիմա" - assert self.locale._format_timeframe("second", 1) == "վայրկյան" - assert self.locale._format_timeframe("seconds", 30) == "30 վայրկյան" - assert self.locale._format_timeframe("minute", 1) == "րոպե" - assert self.locale._format_timeframe("minutes", 40) == "40 րոպե" - assert self.locale._format_timeframe("hour", 1) == "ժամ" - assert self.locale._format_timeframe("hours", 23) == "23 ժամ" - assert self.locale._format_timeframe("day", 1) == "օր" - assert self.locale._format_timeframe("days", 12) == "12 օր" - assert self.locale._format_timeframe("month", 1) == "ամիս" - assert self.locale._format_timeframe("months", 11) == "11 ամիս" - assert self.locale._format_timeframe("year", 1) == "տարին" - assert self.locale._format_timeframe("years", 12) == "12 տարին" + def test_meridians_hy(self): + assert self.locale.meridian(7, "A") == "Ամ" + assert self.locale.meridian(18, "A") == "պ.մ." + assert self.locale.meridian(10, "a") == "Ամ" + assert self.locale.meridian(22, "a") == "պ.մ." def test_format_timeframe(self): # Second(s) From c0057fa48bba51c7d7eabd7f6455a07b941dde9f Mon Sep 17 00:00:00 2001 From: Elaheh Salehi Rizi Date: Thu, 17 Mar 2022 11:19:03 +0100 Subject: [PATCH 14/44] Add more Armenian tests --- tests/test_locales.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/test_locales.py b/tests/test_locales.py index 98f55ddd..37c857e3 100644 --- a/tests/test_locales.py +++ b/tests/test_locales.py @@ -3015,36 +3015,42 @@ def test_format_timeframe(self): assert self.locale._format_timeframe("second", 1) == "վայրկյան" assert self.locale._format_timeframe("seconds", -3) == "3 վայրկյան" assert self.locale._format_timeframe("seconds", 3) == "3 վայրկյան" + assert self.locale._format_timeframe("seconds", 30) == "30 վայրկյան" # Minute(s) assert self.locale._format_timeframe("minute", -1) == "րոպե" assert self.locale._format_timeframe("minute", 1) == "րոպե" assert self.locale._format_timeframe("minutes", -4) == "4 րոպե" assert self.locale._format_timeframe("minutes", 4) == "4 րոպե" + assert self.locale._format_timeframe("minutes", 40) == "40 րոպե" # Hour(s) assert self.locale._format_timeframe("hour", -1) == "ժամ" assert self.locale._format_timeframe("hour", 1) == "ժամ" assert self.locale._format_timeframe("hours", -23) == "23 ժամ" assert self.locale._format_timeframe("hours", 23) == "23 ժամ" + assert self.locale._format_timeframe("hours", 23) == "23 ժամ" # Day(s) assert self.locale._format_timeframe("day", -1) == "օր" assert self.locale._format_timeframe("day", 1) == "օր" assert self.locale._format_timeframe("days", -12) == "12 օր" assert self.locale._format_timeframe("days", 12) == "12 օր" + assert self.locale._format_timeframe("days", 12) == "12 օր" # Month(s) assert self.locale._format_timeframe("month", -1) == "ամիս" assert self.locale._format_timeframe("month", 1) == "ամիս" assert self.locale._format_timeframe("months", -2) == "2 ամիս" assert self.locale._format_timeframe("months", 2) == "2 ամիս" + assert self.locale._format_timeframe("months", 11) == "11 ամիս" # Year(s) assert self.locale._format_timeframe("year", -1) == "տարին" assert self.locale._format_timeframe("year", 1) == "տարին" assert self.locale._format_timeframe("years", -2) == "2 տարին" assert self.locale._format_timeframe("years", 2) == "2 տարին" + assert self.locale._format_timeframe("years", 12) == "12 տարին" def test_weekday(self): dt = arrow.Arrow(2015, 4, 11, 17, 30, 00) From 0543c48ad7979b067313153def6e1103ab49beb6 Mon Sep 17 00:00:00 2001 From: Elaheh Salehi Rizi Date: Thu, 17 Mar 2022 11:46:00 +0100 Subject: [PATCH 15/44] Delete duplicate Armenian tests --- tests/test_locales.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/test_locales.py b/tests/test_locales.py index 37c857e3..3ea584b3 100644 --- a/tests/test_locales.py +++ b/tests/test_locales.py @@ -3029,14 +3029,12 @@ def test_format_timeframe(self): assert self.locale._format_timeframe("hour", 1) == "ժամ" assert self.locale._format_timeframe("hours", -23) == "23 ժամ" assert self.locale._format_timeframe("hours", 23) == "23 ժամ" - assert self.locale._format_timeframe("hours", 23) == "23 ժամ" # Day(s) assert self.locale._format_timeframe("day", -1) == "օր" assert self.locale._format_timeframe("day", 1) == "օր" assert self.locale._format_timeframe("days", -12) == "12 օր" assert self.locale._format_timeframe("days", 12) == "12 օր" - assert self.locale._format_timeframe("days", 12) == "12 օր" # Month(s) assert self.locale._format_timeframe("month", -1) == "ամիս" From 1904804a8d19581397afb2a2b10304e741bcb921 Mon Sep 17 00:00:00 2001 From: Jad Chaar Date: Sun, 1 May 2022 14:36:59 -0700 Subject: [PATCH 16/44] Fix failing CI lint task. (#1107) --- .pre-commit-config.yaml | 8 +++--- arrow/arrow.py | 6 ++--- arrow/constants.py | 2 +- tests/test_arrow.py | 24 ++++++++--------- tests/test_factory.py | 21 +++++++-------- tests/test_parser.py | 57 +++++++++++++++-------------------------- tox.ini | 2 +- 7 files changed, 51 insertions(+), 69 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 28b5f8a0..b352b70b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,7 +2,7 @@ default_language_version: python: python3 repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.1.0 + rev: v4.2.0 hooks: - id: check-ast - id: check-yaml @@ -22,7 +22,7 @@ repos: hooks: - id: isort - repo: https://github.com/asottile/pyupgrade - rev: v2.30.1 + rev: v2.32.0 hooks: - id: pyupgrade args: [--py36-plus] @@ -38,7 +38,7 @@ repos: - id: rst-inline-touching-normal - id: text-unicode-replacement-char - repo: https://github.com/psf/black - rev: 21.12b0 + rev: 22.3.0 hooks: - id: black args: [--safe, --quiet, --target-version=py36] @@ -48,7 +48,7 @@ repos: - id: flake8 additional_dependencies: [flake8-bugbear,flake8-annotations] - repo: https://github.com/pre-commit/mirrors-mypy - rev: 'v0.930' + rev: 'v0.950' hooks: - id: mypy additional_dependencies: [types-python-dateutil] diff --git a/arrow/arrow.py b/arrow/arrow.py index 21b0347f..1ede107f 100644 --- a/arrow/arrow.py +++ b/arrow/arrow.py @@ -1384,7 +1384,7 @@ def dehumanize(self, input_string: str, locale: str = "en_us") -> "Arrow": search_string = search_string.format(r"\d+") # Create search pattern and find within string - pattern = re.compile(fr"(^|\b|\d){search_string}") + pattern = re.compile(rf"(^|\b|\d){search_string}") match = pattern.search(input_string) # If there is no match continue to next iteration @@ -1426,12 +1426,12 @@ def dehumanize(self, input_string: str, locale: str = "en_us") -> "Arrow": # Sign logic future_string = locale_obj.future future_string = future_string.format(".*") - future_pattern = re.compile(fr"^{future_string}$") + future_pattern = re.compile(rf"^{future_string}$") future_pattern_match = future_pattern.findall(input_string) past_string = locale_obj.past past_string = past_string.format(".*") - past_pattern = re.compile(fr"^{past_string}$") + past_pattern = re.compile(rf"^{past_string}$") past_pattern_match = past_pattern.findall(input_string) # If a string contains the now unit, there will be no relative units, hence the need to check if the now unit diff --git a/arrow/constants.py b/arrow/constants.py index 4c6fa5cb..ce29bf1a 100644 --- a/arrow/constants.py +++ b/arrow/constants.py @@ -21,7 +21,7 @@ # Must get max value of ctime on Windows based on architecture (x32 vs x64) # https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/ctime-ctime32-ctime64-wctime-wctime32-wctime64 # Note: this may occur on both 32-bit Linux systems (issue #930) along with Windows systems - is_64bits = sys.maxsize > 2 ** 32 + is_64bits = sys.maxsize > 2**32 _MAX_TIMESTAMP = ( datetime(3000, 1, 1, 23, 59, 59, 999999).timestamp() if is_64bits diff --git a/tests/test_arrow.py b/tests/test_arrow.py index 2e2ffe91..863b3cf1 100644 --- a/tests/test_arrow.py +++ b/tests/test_arrow.py @@ -1909,7 +1909,7 @@ def test_granularity(self): assert self.now.humanize(later4000, granularity="day") == "0 days ago" assert later4000.humanize(self.now, granularity="day") == "in 0 days" - later105 = self.now.shift(seconds=10 ** 5) + later105 = self.now.shift(seconds=10**5) assert self.now.humanize(later105, granularity="hour") == "27 hours ago" assert later105.humanize(self.now, granularity="hour") == "in 27 hours" assert self.now.humanize(later105, granularity="day") == "a day ago" @@ -1921,7 +1921,7 @@ def test_granularity(self): assert self.now.humanize(later105, granularity=["month"]) == "0 months ago" assert later105.humanize(self.now, granularity=["month"]) == "in 0 months" - later106 = self.now.shift(seconds=3 * 10 ** 6) + later106 = self.now.shift(seconds=3 * 10**6) assert self.now.humanize(later106, granularity="day") == "34 days ago" assert later106.humanize(self.now, granularity="day") == "in 34 days" assert self.now.humanize(later106, granularity="week") == "4 weeks ago" @@ -1931,7 +1931,7 @@ def test_granularity(self): assert self.now.humanize(later106, granularity="year") == "0 years ago" assert later106.humanize(self.now, granularity="year") == "in 0 years" - later506 = self.now.shift(seconds=50 * 10 ** 6) + later506 = self.now.shift(seconds=50 * 10**6) assert self.now.humanize(later506, granularity="week") == "82 weeks ago" assert later506.humanize(self.now, granularity="week") == "in 82 weeks" assert self.now.humanize(later506, granularity="month") == "18 months ago" @@ -1943,27 +1943,27 @@ def test_granularity(self): assert self.now.humanize(later1, granularity="quarter") == "0 quarters ago" assert later1.humanize(self.now, granularity="quarter") == "in 0 quarters" - later107 = self.now.shift(seconds=10 ** 7) + later107 = self.now.shift(seconds=10**7) assert self.now.humanize(later107, granularity="quarter") == "a quarter ago" assert later107.humanize(self.now, granularity="quarter") == "in a quarter" - later207 = self.now.shift(seconds=2 * 10 ** 7) + later207 = self.now.shift(seconds=2 * 10**7) assert self.now.humanize(later207, granularity="quarter") == "2 quarters ago" assert later207.humanize(self.now, granularity="quarter") == "in 2 quarters" - later307 = self.now.shift(seconds=3 * 10 ** 7) + later307 = self.now.shift(seconds=3 * 10**7) assert self.now.humanize(later307, granularity="quarter") == "3 quarters ago" assert later307.humanize(self.now, granularity="quarter") == "in 3 quarters" - later377 = self.now.shift(seconds=3.7 * 10 ** 7) + later377 = self.now.shift(seconds=3.7 * 10**7) assert self.now.humanize(later377, granularity="quarter") == "4 quarters ago" assert later377.humanize(self.now, granularity="quarter") == "in 4 quarters" - later407 = self.now.shift(seconds=4 * 10 ** 7) + later407 = self.now.shift(seconds=4 * 10**7) assert self.now.humanize(later407, granularity="quarter") == "5 quarters ago" assert later407.humanize(self.now, granularity="quarter") == "in 5 quarters" - later108 = self.now.shift(seconds=10 ** 8) + later108 = self.now.shift(seconds=10**8) assert self.now.humanize(later108, granularity="year") == "3 years ago" assert later108.humanize(self.now, granularity="year") == "in 3 years" - later108onlydistance = self.now.shift(seconds=10 ** 8) + later108onlydistance = self.now.shift(seconds=10**8) assert ( self.now.humanize( later108onlydistance, only_distance=True, granularity="year" @@ -2012,7 +2012,7 @@ def test_multiple_granularity(self): == "0 days an hour and 6 minutes ago" ) - later105 = self.now.shift(seconds=10 ** 5) + later105 = self.now.shift(seconds=10**5) assert ( self.now.humanize(later105, granularity=["hour", "day", "minute"]) == "a day 3 hours and 46 minutes ago" @@ -2020,7 +2020,7 @@ def test_multiple_granularity(self): with pytest.raises(ValueError): self.now.humanize(later105, granularity=["error", "second"]) - later108onlydistance = self.now.shift(seconds=10 ** 8) + later108onlydistance = self.now.shift(seconds=10**8) assert ( self.now.humanize( later108onlydistance, only_distance=True, granularity=["year"] diff --git a/tests/test_factory.py b/tests/test_factory.py index 53bba20d..f368126c 100644 --- a/tests/test_factory.py +++ b/tests/test_factory.py @@ -359,18 +359,15 @@ def test_three_args(self): def test_full_kwargs(self): - assert ( - self.factory.get( - year=2016, - month=7, - day=14, - hour=7, - minute=16, - second=45, - microsecond=631092, - ) - == datetime(2016, 7, 14, 7, 16, 45, 631092, tzinfo=tz.tzutc()) - ) + assert self.factory.get( + year=2016, + month=7, + day=14, + hour=7, + minute=16, + second=45, + microsecond=631092, + ) == datetime(2016, 7, 14, 7, 16, 45, 631092, tzinfo=tz.tzutc()) def test_three_kwargs(self): diff --git a/tests/test_parser.py b/tests/test_parser.py index 4a4cfe41..bb4ab148 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -145,24 +145,18 @@ def test_YY_and_YYYY_format_list(self): 2019, 1, 15 ) - assert ( - self.parser.parse( - "15/01/2019T04:05:06.789120Z", - ["D/M/YYThh:mm:ss.SZ", "D/M/YYYYThh:mm:ss.SZ"], - ) - == datetime(2019, 1, 15, 4, 5, 6, 789120, tzinfo=tz.tzutc()) - ) + assert self.parser.parse( + "15/01/2019T04:05:06.789120Z", + ["D/M/YYThh:mm:ss.SZ", "D/M/YYYYThh:mm:ss.SZ"], + ) == datetime(2019, 1, 15, 4, 5, 6, 789120, tzinfo=tz.tzutc()) # regression test for issue #447 def test_timestamp_format_list(self): # should not match on the "X" token - assert ( - self.parser.parse( - "15 Jul 2000", - ["MM/DD/YYYY", "YYYY-MM-DD", "X", "DD-MMMM-YYYY", "D MMM YYYY"], - ) - == datetime(2000, 7, 15) - ) + assert self.parser.parse( + "15 Jul 2000", + ["MM/DD/YYYY", "YYYY-MM-DD", "X", "DD-MMMM-YYYY", "D MMM YYYY"], + ) == datetime(2000, 7, 15) with pytest.raises(ParserError): self.parser.parse("15 Jul", "X") @@ -503,21 +497,15 @@ def test_parse_with_extra_words_at_start_and_end_valid(self): "2016-05-16T04:05:06.789120 blah", "YYYY-MM-DDThh:mm:ss.S" ) == datetime(2016, 5, 16, 4, 5, 6, 789120) - assert ( - self.parser.parse( - "Meet me at 2016-05-16T04:05:06.789120 at the restaurant.", - "YYYY-MM-DDThh:mm:ss.S", - ) - == datetime(2016, 5, 16, 4, 5, 6, 789120) - ) + assert self.parser.parse( + "Meet me at 2016-05-16T04:05:06.789120 at the restaurant.", + "YYYY-MM-DDThh:mm:ss.S", + ) == datetime(2016, 5, 16, 4, 5, 6, 789120) - assert ( - self.parser.parse( - "Meet me at 2016-05-16 04:05:06.789120 at the restaurant.", - "YYYY-MM-DD hh:mm:ss.S", - ) - == datetime(2016, 5, 16, 4, 5, 6, 789120) - ) + assert self.parser.parse( + "Meet me at 2016-05-16 04:05:06.789120 at the restaurant.", + "YYYY-MM-DD hh:mm:ss.S", + ) == datetime(2016, 5, 16, 4, 5, 6, 789120) # regression test for issue #701 # tests cases of a partial match surrounded by punctuation @@ -783,14 +771,11 @@ def test_parse_normalize_whitespace(self): with pytest.raises(ParserError): self.parser.parse("Jun 1 2005 1:33PM", "MMM D YYYY H:mmA") - assert ( - self.parser.parse( - "\t 2013-05-05 T \n 12:30:45\t123456 \t \n", - "YYYY-MM-DD T HH:mm:ss S", - normalize_whitespace=True, - ) - == datetime(2013, 5, 5, 12, 30, 45, 123456) - ) + assert self.parser.parse( + "\t 2013-05-05 T \n 12:30:45\t123456 \t \n", + "YYYY-MM-DD T HH:mm:ss S", + normalize_whitespace=True, + ) == datetime(2013, 5, 5, 12, 30, 45, 123456) with pytest.raises(ParserError): self.parser.parse( diff --git a/tox.ini b/tox.ini index fefa3e7e..c51432a2 100644 --- a/tox.ini +++ b/tox.ini @@ -45,4 +45,4 @@ include_trailing_comma = true [flake8] per-file-ignores = arrow/__init__.py:F401,tests/*:ANN001,ANN201 -ignore = E203,E501,W503,ANN101,ANN102 +ignore = E203,E501,W503,ANN101,ANN102,ANN401 From f98ad731779c38e01920d74fef9c4b4d24342f89 Mon Sep 17 00:00:00 2001 From: cyriaka90 Date: Mon, 2 May 2022 01:40:19 +0200 Subject: [PATCH 17/44] Add Laotian locale (#1105) Co-authored-by: Jad Chaar --- arrow/constants.py | 2 + arrow/locales.py | 108 ++++++++++++++++++++++++++++++++++++++++++ tests/test_arrow.py | 2 + tests/test_locales.py | 57 ++++++++++++++++++++++ 4 files changed, 169 insertions(+) diff --git a/arrow/constants.py b/arrow/constants.py index ce29bf1a..b06f7b13 100644 --- a/arrow/constants.py +++ b/arrow/constants.py @@ -166,6 +166,8 @@ "ka-ge", "kk", "kk-kz", + # "lo", + # "lo-la", "am", "am-et", } diff --git a/arrow/locales.py b/arrow/locales.py index d5652370..f6723741 100644 --- a/arrow/locales.py +++ b/arrow/locales.py @@ -3971,6 +3971,114 @@ def _format_relative( return relative_string +class LaotianLocale(Locale): + + names = ["lo", "lo-la"] + + past = "{0} ກ່ອນຫນ້ານີ້" + future = "ໃນ {0}" + + timeframes = { + "now": "ດຽວນີ້", + "second": "ວິນາທີ", + "seconds": "{0} ວິນາທີ", + "minute": "ນາທີ", + "minutes": "{0} ນາທີ", + "hour": "ຊົ່ວໂມງ", + "hours": "{0} ຊົ່ວໂມງ", + "day": "ມື້", + "days": "{0} ມື້", + "week": "ອາທິດ", + "weeks": "{0} ອາທິດ", + "month": "ເດືອນ", + "months": "{0} ເດືອນ", + "year": "ປີ", + "years": "{0} ປີ", + } + + month_names = [ + "", + "ມັງກອນ", # mangkon + "ກຸມພາ", # kumpha + "ມີນາ", # mina + "ເມສາ", # mesa + "ພຶດສະພາ", # phudsapha + "ມິຖຸນາ", # mithuna + "ກໍລະກົດ", # kolakod + "ສິງຫາ", # singha + "ກັນຍາ", # knaia + "ຕຸລາ", # tula + "ພະຈິກ", # phachik + "ທັນວາ", # thanuaa + ] + month_abbreviations = [ + "", + "ມັງກອນ", + "ກຸມພາ", + "ມີນາ", + "ເມສາ", + "ພຶດສະພາ", + "ມິຖຸນາ", + "ກໍລະກົດ", + "ສິງຫາ", + "ກັນຍາ", + "ຕຸລາ", + "ພະຈິກ", + "ທັນວາ", + ] + + day_names = [ + "", + "ວັນຈັນ", # vanchan + "ວັນອັງຄານ", # vnoangkhan + "ວັນພຸດ", # vanphud + "ວັນພະຫັດ", # vanphahad + "ວັນ​ສຸກ", # vansuk + "ວັນເສົາ", # vansao + "ວັນອາທິດ", # vnoathid + ] + day_abbreviations = [ + "", + "ວັນຈັນ", + "ວັນອັງຄານ", + "ວັນພຸດ", + "ວັນພະຫັດ", + "ວັນ​ສຸກ", + "ວັນເສົາ", + "ວັນອາທິດ", + ] + + BE_OFFSET = 543 + + def year_full(self, year: int) -> str: + """Lao always use Buddhist Era (BE) which is CE + 543""" + year += self.BE_OFFSET + return f"{year:04d}" + + def year_abbreviation(self, year: int) -> str: + """Lao always use Buddhist Era (BE) which is CE + 543""" + year += self.BE_OFFSET + return f"{year:04d}"[2:] + + def _format_relative( + self, + humanized: str, + timeframe: TimeFrameLiteral, + delta: Union[float, int], + ) -> str: + """Lao normally doesn't have any space between words""" + if timeframe == "now": + return humanized + + direction = self.past if delta < 0 else self.future + relative_string = direction.format(humanized) + + if timeframe == "seconds": + relative_string = relative_string.replace(" ", "") + + return relative_string + + class BengaliLocale(Locale): names = ["bn", "bn-bd", "bn-in"] diff --git a/tests/test_arrow.py b/tests/test_arrow.py index 863b3cf1..3809f128 100644 --- a/tests/test_arrow.py +++ b/tests/test_arrow.py @@ -2470,6 +2470,8 @@ def locale_list_no_weeks() -> List[str]: "ka-ge", "kk", "kk-kz", + # "lo", + # "lo-la", ] return tested_langs diff --git a/tests/test_locales.py b/tests/test_locales.py index 0e42074d..ed62dc80 100644 --- a/tests/test_locales.py +++ b/tests/test_locales.py @@ -1294,6 +1294,63 @@ def test_ordinal_number(self): assert self.locale.ordinal_number(1) == "1a" +@pytest.mark.usefixtures("lang_locale") +class TestLaotianLocale: + def test_year_full(self): + assert self.locale.year_full(2015) == "2558" + + def test_year_abbreviation(self): + assert self.locale.year_abbreviation(2015) == "58" + + def test_format_relative_now(self): + result = self.locale._format_relative("ດຽວນີ້", "now", 0) + assert result == "ດຽວນີ້" + + def test_format_relative_past(self): + result = self.locale._format_relative("1 ຊົ່ວໂມງ", "hour", 1) + assert result == "ໃນ 1 ຊົ່ວໂມງ" + result = self.locale._format_relative("{0} ຊົ່ວໂມງ", "hours", 2) + assert result == "ໃນ {0} ຊົ່ວໂມງ" + result = self.locale._format_relative("ວິນາທີ", "seconds", 42) + assert result == "ໃນວິນາທີ" + + def test_format_relative_future(self): + result = self.locale._format_relative("1 ຊົ່ວໂມງ", "hour", -1) + assert result == "1 ຊົ່ວໂມງ ກ່ອນຫນ້ານີ້" + + def test_format_timeframe(self): + # minute(s) + assert self.locale._format_timeframe("minute", 1) == "ນາທີ" + assert self.locale._format_timeframe("minute", -1) == "ນາທີ" + assert self.locale._format_timeframe("minutes", 7) == "7 ນາທີ" + assert self.locale._format_timeframe("minutes", -20) == "20 ນາທີ" + # day(s) + assert self.locale._format_timeframe("day", 1) == "ມື້" + assert self.locale._format_timeframe("day", -1) == "ມື້" + assert self.locale._format_timeframe("days", 7) == "7 ມື້" + assert self.locale._format_timeframe("days", -20) == "20 ມື້" + # week(s) + assert self.locale._format_timeframe("week", 1) == "ອາທິດ" + assert self.locale._format_timeframe("week", -1) == "ອາທິດ" + assert self.locale._format_timeframe("weeks", 7) == "7 ອາທິດ" + assert self.locale._format_timeframe("weeks", -20) == "20 ອາທິດ" + # month(s) + assert self.locale._format_timeframe("month", 1) == "ເດືອນ" + assert self.locale._format_timeframe("month", -1) == "ເດືອນ" + assert self.locale._format_timeframe("months", 7) == "7 ເດືອນ" + assert self.locale._format_timeframe("months", -20) == "20 ເດືອນ" + # year(s) + assert self.locale._format_timeframe("year", 1) == "ປີ" + assert self.locale._format_timeframe("year", -1) == "ປີ" + assert self.locale._format_timeframe("years", 7) == "7 ປີ" + assert self.locale._format_timeframe("years", -20) == "20 ປີ" + + def test_weekday(self): + dt = arrow.Arrow(2015, 4, 11, 17, 30, 00) + assert self.locale.day_name(dt.isoweekday()) == "ວັນເສົາ" + assert self.locale.day_abbreviation(dt.isoweekday()) == "ວັນເສົາ" + + @pytest.mark.usefixtures("lang_locale") class TestThaiLocale: def test_year_full(self): From 4357f8c8dbdf8cf8c01a3f230873e19bd8a1a4d0 Mon Sep 17 00:00:00 2001 From: Soo Hur Date: Mon, 2 May 2022 12:14:41 -0700 Subject: [PATCH 18/44] Add Uzbek (#1098) --- arrow/constants.py | 2 + arrow/locales.py | 127 ++++++++++++++++++++++-------------------- tests/test_arrow.py | 4 ++ tests/test_locales.py | 79 ++++++++++++++++++++++++++ 4 files changed, 152 insertions(+), 60 deletions(-) diff --git a/arrow/constants.py b/arrow/constants.py index b06f7b13..c0fef8cd 100644 --- a/arrow/constants.py +++ b/arrow/constants.py @@ -170,4 +170,6 @@ # "lo-la", "am", "am-et", + "uz", + "uz-uz", } diff --git a/arrow/locales.py b/arrow/locales.py index f6723741..282ed0dc 100644 --- a/arrow/locales.py +++ b/arrow/locales.py @@ -45,7 +45,6 @@ str, Sequence[str], Mapping[str, str], Mapping[str, Sequence[str]] ] - _locale_map: Dict[str, Type["Locale"]] = {} @@ -296,7 +295,6 @@ def _format_relative( class EnglishLocale(Locale): - names = [ "en", "en-us", @@ -560,7 +558,6 @@ def _ordinal_number(self, n: int) -> str: class FrenchBaseLocale(Locale): - past = "il y a {0}" future = "dans {0}" and_word = "et" @@ -622,7 +619,6 @@ def _ordinal_number(self, n: int) -> str: class FrenchLocale(FrenchBaseLocale, Locale): - names = ["fr", "fr-fr"] month_abbreviations = [ @@ -643,7 +639,6 @@ class FrenchLocale(FrenchBaseLocale, Locale): class FrenchCanadianLocale(FrenchBaseLocale, Locale): - names = ["fr-ca"] month_abbreviations = [ @@ -664,7 +659,6 @@ class FrenchCanadianLocale(FrenchBaseLocale, Locale): class GreekLocale(Locale): - names = ["el", "el-gr"] past = "{0} πριν" @@ -734,7 +728,6 @@ class GreekLocale(Locale): class JapaneseLocale(Locale): - names = ["ja", "ja-jp"] past = "{0}前" @@ -795,7 +788,6 @@ class JapaneseLocale(Locale): class SwedishLocale(Locale): - names = ["sv", "sv-se"] past = "för {0} sen" @@ -865,7 +857,6 @@ class SwedishLocale(Locale): class FinnishLocale(Locale): - names = ["fi", "fi-fi"] # The finnish grammar is very complex, and its hard to convert @@ -952,7 +943,6 @@ def _ordinal_number(self, n: int) -> str: class ChineseCNLocale(Locale): - names = ["zh", "zh-cn"] past = "{0}前" @@ -1012,7 +1002,6 @@ class ChineseCNLocale(Locale): class ChineseTWLocale(Locale): - names = ["zh-tw"] past = "{0}前" @@ -1073,7 +1062,6 @@ class ChineseTWLocale(Locale): class HongKongLocale(Locale): - names = ["zh-hk"] past = "{0}前" @@ -1133,7 +1121,6 @@ class HongKongLocale(Locale): class KoreanLocale(Locale): - names = ["ko", "ko-kr"] past = "{0} 전" @@ -1229,7 +1216,6 @@ def _format_relative( # derived locale types & implementations. class DutchLocale(Locale): - names = ["nl", "nl-nl"] past = "{0} geleden" @@ -1318,7 +1304,6 @@ def _format_timeframe(self, timeframe: TimeFrameLiteral, delta: int) -> str: class BelarusianLocale(SlavicBaseLocale): - names = ["be", "be-by"] past = "{0} таму" @@ -1397,7 +1382,6 @@ class BelarusianLocale(SlavicBaseLocale): class PolishLocale(SlavicBaseLocale): - names = ["pl", "pl-pl"] past = "{0} temu" @@ -1488,7 +1472,6 @@ class PolishLocale(SlavicBaseLocale): class RussianLocale(SlavicBaseLocale): - names = ["ru", "ru-ru"] past = "{0} назад" @@ -1579,7 +1562,6 @@ class RussianLocale(SlavicBaseLocale): class AfrikaansLocale(Locale): - names = ["af", "af-nl"] past = "{0} gelede" @@ -1646,7 +1628,6 @@ class AfrikaansLocale(Locale): class BulgarianLocale(SlavicBaseLocale): - names = ["bg", "bg-bg"] past = "{0} назад" @@ -1725,7 +1706,6 @@ class BulgarianLocale(SlavicBaseLocale): class UkrainianLocale(SlavicBaseLocale): - names = ["ua", "uk", "uk-ua"] past = "{0} тому" @@ -1903,7 +1883,6 @@ class MacedonianLocale(SlavicBaseLocale): class GermanBaseLocale(Locale): - past = "vor {0}" future = "in {0}" and_word = "und" @@ -2009,17 +1988,14 @@ def describe( class GermanLocale(GermanBaseLocale, Locale): - names = ["de", "de-de"] class SwissLocale(GermanBaseLocale, Locale): - names = ["de-ch"] class AustrianLocale(GermanBaseLocale, Locale): - names = ["de-at"] month_names = [ @@ -2040,7 +2016,6 @@ class AustrianLocale(GermanBaseLocale, Locale): class NorwegianLocale(Locale): - names = ["nb", "nb-no"] past = "for {0} siden" @@ -2112,7 +2087,6 @@ def _ordinal_number(self, n: int) -> str: class NewNorwegianLocale(Locale): - names = ["nn", "nn-no"] past = "for {0} sidan" @@ -2259,7 +2233,6 @@ class BrazilianPortugueseLocale(PortugueseLocale): class TagalogLocale(Locale): - names = ["tl", "tl-ph"] past = "nakaraang {0}" @@ -2333,7 +2306,6 @@ def _ordinal_number(self, n: int) -> str: class VietnameseLocale(Locale): - names = ["vi", "vi-vn"] past = "{0} trước" @@ -2402,7 +2374,6 @@ class VietnameseLocale(Locale): class TurkishLocale(Locale): - names = ["tr", "tr-tr"] past = "{0} önce" @@ -2474,7 +2445,6 @@ class TurkishLocale(Locale): class AzerbaijaniLocale(Locale): - names = ["az", "az-az"] past = "{0} əvvəl" @@ -2862,7 +2832,6 @@ def _format_timeframe(self, timeframe: TimeFrameLiteral, delta: int) -> str: class DanishLocale(Locale): - names = ["da", "da-dk"] past = "for {0} siden" @@ -2935,7 +2904,6 @@ def _ordinal_number(self, n: int) -> str: class MalayalamLocale(Locale): - names = ["ml"] past = "{0} മുമ്പ്" @@ -3009,7 +2977,6 @@ class MalayalamLocale(Locale): class HindiLocale(Locale): - names = ["hi", "hi-in"] past = "{0} पहले" @@ -3338,7 +3305,6 @@ def _format_timeframe(self, timeframe: TimeFrameLiteral, delta: int) -> str: class FarsiLocale(Locale): - names = ["fa", "fa-ir"] past = "{0} قبل" @@ -3412,7 +3378,6 @@ class FarsiLocale(Locale): class HebrewLocale(Locale): - names = ["he", "he-il"] past = "לפני {0}" @@ -3523,7 +3488,6 @@ def describe_multi( class MarathiLocale(Locale): - names = ["mr"] past = "{0} आधी" @@ -3730,7 +3694,6 @@ class BasqueLocale(Locale): class HungarianLocale(Locale): - names = ["hu", "hu-hu"] past = "{0} ezelőtt" @@ -3882,7 +3845,6 @@ def _ordinal_number(self, n: int) -> str: class ThaiLocale(Locale): - names = ["th", "th-th"] past = "{0} ที่ผ่านมา" @@ -4080,7 +4042,6 @@ def _format_relative( class BengaliLocale(Locale): - names = ["bn", "bn-bd", "bn-in"] past = "{0} আগে" @@ -4161,7 +4122,6 @@ def _ordinal_number(self, n: int) -> str: class RomanshLocale(Locale): - names = ["rm", "rm-ch"] past = "avant {0}" @@ -4368,7 +4328,6 @@ class SlovenianLocale(Locale): class IndonesianLocale(Locale): - names = ["id", "id-id"] past = "{0} yang lalu" @@ -4588,7 +4547,6 @@ def _format_timeframe(self, timeframe: TimeFrameLiteral, delta: int) -> str: class LatvianLocale(Locale): - names = ["lv", "lv-lv"] past = "pirms {0}" @@ -4669,7 +4627,6 @@ class LatvianLocale(Locale): class SwahiliLocale(Locale): - names = [ "sw", "sw-ke", @@ -4754,7 +4711,6 @@ class SwahiliLocale(Locale): class CroatianLocale(Locale): - names = ["hr", "hr-hr"] past = "prije {0}" @@ -4846,7 +4802,6 @@ def _format_timeframe(self, timeframe: TimeFrameLiteral, delta: int) -> str: class LatinLocale(Locale): - names = ["la", "la-va"] past = "ante {0}" @@ -4927,7 +4882,6 @@ class LatinLocale(Locale): class LithuanianLocale(Locale): - names = ["lt", "lt-lt"] past = "prieš {0}" @@ -5008,7 +4962,6 @@ class LithuanianLocale(Locale): class MalayLocale(Locale): - names = ["ms", "ms-my", "ms-bn"] past = "{0} yang lalu" @@ -5089,7 +5042,6 @@ class MalayLocale(Locale): class MalteseLocale(Locale): - names = ["mt", "mt-mt"] past = "{0} ilu" @@ -5181,7 +5133,6 @@ def _format_timeframe(self, timeframe: TimeFrameLiteral, delta: int) -> str: class SamiLocale(Locale): - names = ["se", "se-fi", "se-no", "se-se"] past = "{0} dassái" @@ -5261,7 +5212,6 @@ class SamiLocale(Locale): class OdiaLocale(Locale): - names = ["or", "or-in"] past = "{0} ପୂର୍ବେ" @@ -5352,7 +5302,6 @@ def _ordinal_number(self, n: int) -> str: class SerbianLocale(Locale): - names = ["sr", "sr-rs", "sr-sp"] past = "pre {0}" @@ -5444,7 +5393,6 @@ def _format_timeframe(self, timeframe: TimeFrameLiteral, delta: int) -> str: class LuxembourgishLocale(Locale): - names = ["lb", "lb-lu"] past = "virun {0}" @@ -5535,7 +5483,6 @@ def describe( delta: Union[int, float] = 0, only_distance: bool = False, ) -> str: - if not only_distance: return super().describe(timeframe, delta, only_distance) @@ -5546,7 +5493,6 @@ def describe( class ZuluLocale(Locale): - names = ["zu", "zu-za"] past = "{0} edlule" @@ -5644,7 +5590,6 @@ def _format_timeframe(self, timeframe: TimeFrameLiteral, delta: int) -> str: class TamilLocale(Locale): - names = ["ta", "ta-in", "ta-lk"] past = "{0} நேரத்திற்கு முன்பு" @@ -5732,7 +5677,6 @@ def _ordinal_number(self, n: int) -> str: class AlbanianLocale(Locale): - names = ["sq", "sq-al"] past = "{0} më parë" @@ -5813,7 +5757,6 @@ class AlbanianLocale(Locale): class GeorgianLocale(Locale): - names = ["ka", "ka-ge"] past = "{0} წინ" # ts’in @@ -5898,7 +5841,6 @@ class GeorgianLocale(Locale): class SinhalaLocale(Locale): - names = ["si", "si-lk"] past = "{0}ට පෙර" @@ -6062,7 +6004,6 @@ def describe( class UrduLocale(Locale): - names = ["ur", "ur-pk"] past = "پہلے {0}" @@ -6143,7 +6084,6 @@ class UrduLocale(Locale): class KazakhLocale(Locale): - names = ["kk", "kk-kz"] past = "{0} бұрын" @@ -6379,3 +6319,70 @@ def describe( humanized = self.timeframes_only_distance[timeframe].format(trunc(abs(delta))) return humanized + + +class UzbekLocale(Locale): + names = ["uz", "uz-uz"] + past = "{0}dan avval" + future = "{0}dan keyin" + timeframes = { + "now": "hozir", + "second": "bir soniya", + "seconds": "{0} soniya", + "minute": "bir daqiqa", + "minutes": "{0} daqiqa", + "hour": "bir soat", + "hours": "{0} soat", + "day": "bir kun", + "days": "{0} kun", + "week": "bir hafta", + "weeks": "{0} hafta", + "month": "bir oy", + "months": "{0} oy", + "year": "bir yil", + "years": "{0} yil", + } + + month_names = [ + "", + "Yanvar", + "Fevral", + "Mart", + "Aprel", + "May", + "Iyun", + "Iyul", + "Avgust", + "Sentyabr", + "Oktyabr", + "Noyabr", + "Dekabr", + ] + + month_abbreviations = [ + "", + "Yan", + "Fev", + "Mar", + "Apr", + "May", + "Iyn", + "Iyl", + "Avg", + "Sen", + "Okt", + "Noy", + "Dek", + ] + + day_names = [ + "", + "Dushanba", + "Seshanba", + "Chorshanba", + "Payshanba", + "Juma", + "Shanba", + "Yakshanba", + ] + day_abbreviations = ["", "Dush", "Sesh", "Chor", "Pay", "Jum", "Shan", "Yak"] diff --git a/tests/test_arrow.py b/tests/test_arrow.py index 3809f128..2289c583 100644 --- a/tests/test_arrow.py +++ b/tests/test_arrow.py @@ -2470,6 +2470,8 @@ def locale_list_no_weeks() -> List[str]: "ka-ge", "kk", "kk-kz", + "uz", + "uz-uz", # "lo", # "lo-la", ] @@ -2546,6 +2548,8 @@ def locale_list_with_weeks() -> List[str]: "ta-lk", "kk", "kk-kz", + "uz", + "uz-uz", ] return tested_langs diff --git a/tests/test_locales.py b/tests/test_locales.py index ed62dc80..2927a492 100644 --- a/tests/test_locales.py +++ b/tests/test_locales.py @@ -3052,3 +3052,82 @@ def test_weekday(self): dt = arrow.Arrow(2015, 4, 11, 17, 30, 00) assert self.locale.day_name(dt.isoweekday()) == "ቅዳሜ" assert self.locale.day_abbreviation(dt.isoweekday()) == "ዓ" + + +@pytest.mark.usefixtures("lang_locale") +class TestUzbekLocale: + def test_singles_mk(self): + assert self.locale._format_timeframe("second", 1) == "bir soniya" + assert self.locale._format_timeframe("minute", 1) == "bir daqiqa" + assert self.locale._format_timeframe("hour", 1) == "bir soat" + assert self.locale._format_timeframe("day", 1) == "bir kun" + assert self.locale._format_timeframe("week", 1) == "bir hafta" + assert self.locale._format_timeframe("month", 1) == "bir oy" + assert self.locale._format_timeframe("year", 1) == "bir yil" + + def test_describe_mk(self): + assert self.locale.describe("second", only_distance=True) == "bir soniya" + assert ( + self.locale.describe("second", only_distance=False) == "bir soniyadan keyin" + ) + assert self.locale.describe("minute", only_distance=True) == "bir daqiqa" + assert ( + self.locale.describe("minute", only_distance=False) == "bir daqiqadan keyin" + ) + assert self.locale.describe("hour", only_distance=True) == "bir soat" + assert self.locale.describe("hour", only_distance=False) == "bir soatdan keyin" + assert self.locale.describe("day", only_distance=True) == "bir kun" + assert self.locale.describe("day", only_distance=False) == "bir kundan keyin" + assert self.locale.describe("week", only_distance=True) == "bir hafta" + assert self.locale.describe("week", only_distance=False) == "bir haftadan keyin" + assert self.locale.describe("month", only_distance=True) == "bir oy" + assert self.locale.describe("month", only_distance=False) == "bir oydan keyin" + assert self.locale.describe("year", only_distance=True) == "bir yil" + assert self.locale.describe("year", only_distance=False) == "bir yildan keyin" + + def test_relative_mk(self): + assert self.locale._format_relative("hozir", "now", 0) == "hozir" + assert ( + self.locale._format_relative("1 soniya", "seconds", 1) + == "1 soniyadan keyin" + ) + assert ( + self.locale._format_relative("1 soniya", "seconds", -1) + == "1 soniyadan avval" + ) + assert ( + self.locale._format_relative("1 daqiqa", "minutes", 1) + == "1 daqiqadan keyin" + ) + assert ( + self.locale._format_relative("1 daqiqa", "minutes", -1) + == "1 daqiqadan avval" + ) + assert self.locale._format_relative("1 soat", "hours", 1) == "1 soatdan keyin" + assert self.locale._format_relative("1 soat", "hours", -1) == "1 soatdan avval" + assert self.locale._format_relative("1 kun", "days", 1) == "1 kundan keyin" + assert self.locale._format_relative("1 kun", "days", -1) == "1 kundan avval" + assert self.locale._format_relative("1 hafta", "weeks", 1) == "1 haftadan keyin" + assert ( + self.locale._format_relative("1 hafta", "weeks", -1) == "1 haftadan avval" + ) + assert self.locale._format_relative("1 oy", "months", 1) == "1 oydan keyin" + assert self.locale._format_relative("1 oy", "months", -1) == "1 oydan avval" + assert self.locale._format_relative("1 yil", "years", 1) == "1 yildan keyin" + assert self.locale._format_relative("1 yil", "years", -1) == "1 yildan avval" + + def test_plurals_mk(self): + assert self.locale._format_timeframe("now", 0) == "hozir" + assert self.locale._format_timeframe("second", 1) == "bir soniya" + assert self.locale._format_timeframe("seconds", 30) == "30 soniya" + assert self.locale._format_timeframe("minute", 1) == "bir daqiqa" + assert self.locale._format_timeframe("minutes", 40) == "40 daqiqa" + assert self.locale._format_timeframe("hour", 1) == "bir soat" + assert self.locale._format_timeframe("hours", 23) == "23 soat" + assert self.locale._format_timeframe("days", 12) == "12 kun" + assert self.locale._format_timeframe("week", 1) == "bir hafta" + assert self.locale._format_timeframe("weeks", 38) == "38 hafta" + assert self.locale._format_timeframe("month", 1) == "bir oy" + assert self.locale._format_timeframe("months", 11) == "11 oy" + assert self.locale._format_timeframe("year", 1) == "bir yil" + assert self.locale._format_timeframe("years", 12) == "12 yil" From fbe19bd177e17bb58cc23e40fea6f0d695ee436c Mon Sep 17 00:00:00 2001 From: Chris <30196510+systemcatch@users.noreply.github.com> Date: Wed, 18 May 2022 19:47:48 +0100 Subject: [PATCH 19/44] Fix syntax in locales --- arrow/locales.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arrow/locales.py b/arrow/locales.py index a94ddea7..c25b98a2 100644 --- a/arrow/locales.py +++ b/arrow/locales.py @@ -6366,7 +6366,7 @@ class ArmenianLocale(Locale): "հոկտեմբեր", "նոյեմբեր", "դեկտեմբեր", - } + ] month_abbreviations = [ "", From de1c5bdca7279311fd2a9d57c38b5f7ec450ddce Mon Sep 17 00:00:00 2001 From: Chris <30196510+systemcatch@users.noreply.github.com> Date: Wed, 18 May 2022 19:47:58 +0100 Subject: [PATCH 20/44] Fix syntax in locales --- arrow/locales.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arrow/locales.py b/arrow/locales.py index c25b98a2..d0f31448 100644 --- a/arrow/locales.py +++ b/arrow/locales.py @@ -6382,7 +6382,7 @@ class ArmenianLocale(Locale): "հոկտեմբեր", "նոյեմբեր", "դեկտեմբեր", - } + ] day_names = [ "", From 3fa1b92d3168ac734f1ab5e47a98d711d3bf566a Mon Sep 17 00:00:00 2001 From: ElahehAx Date: Thu, 2 Jun 2022 14:58:07 +0200 Subject: [PATCH 21/44] Add Armenian locale --- arrow/locales.py | 13 ++++++------- tests/test_locales.py | 4 ++-- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/arrow/locales.py b/arrow/locales.py index d0f31448..3627497f 100644 --- a/arrow/locales.py +++ b/arrow/locales.py @@ -6325,7 +6325,7 @@ class ArmenianLocale(Locale): names = ["hy", "hy-am"] past = "{0} առաջ" future = "{0}ից" - and_word = "Եվ" + and_word = "Եվ" # Yev timeframes = { "now": "հիմա", @@ -6351,7 +6351,7 @@ class ArmenianLocale(Locale): "AM": "Ամ", "PM": "պ.մ.", } - + month_names = [ "", "հունվար", @@ -6383,7 +6383,7 @@ class ArmenianLocale(Locale): "նոյեմբեր", "դեկտեմբեր", ] - + day_names = [ "", "երկուշաբթի", @@ -6393,7 +6393,7 @@ class ArmenianLocale(Locale): "ուրբաթ", "շաբաթ", "կիրակի", - ] + ] day_abbreviations = [ "", @@ -6406,7 +6406,7 @@ class ArmenianLocale(Locale): "կիր.", ] - + class UzbekLocale(Locale): names = ["uz", "uz-uz"] past = "{0}dan avval" @@ -6471,6 +6471,5 @@ class UzbekLocale(Locale): "Shanba", "Yakshanba", ] - - day_abbreviations = ["", "Dush", "Sesh", "Chor", "Pay", "Jum", "Shan", "Yak"] + day_abbreviations = ["", "Dush", "Sesh", "Chor", "Pay", "Jum", "Shan", "Yak"] diff --git a/tests/test_locales.py b/tests/test_locales.py index 4df9667c..5ff00c1b 100644 --- a/tests/test_locales.py +++ b/tests/test_locales.py @@ -3111,8 +3111,8 @@ def test_weekday(self): dt = arrow.Arrow(2015, 4, 11, 17, 30, 00) assert self.locale.day_name(dt.isoweekday()) == "շաբաթ" assert self.locale.day_abbreviation(dt.isoweekday()) == "շաբ." - - + + @pytest.mark.usefixtures("lang_locale") class TestUzbekLocale: def test_singles_mk(self): From 7b5c1aa73e97c98ea7a10ba5a4743fc9d3e41a2e Mon Sep 17 00:00:00 2001 From: "Kristijan \"Fremen\" Velkovski" Date: Wed, 22 Jun 2022 19:55:44 -0500 Subject: [PATCH 22/44] Sphinx language set to "en" as they no longer support None as of 5.x releases. (#1114) --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index f106cb7f..68800bca 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -30,7 +30,7 @@ source_suffix = ".rst" pygments_style = "sphinx" -language = None +language = "en" # -- Options for HTML output ------------------------------------------------- From 8842f8c3263d1f1219c189a0500aa67abdd0a214 Mon Sep 17 00:00:00 2001 From: Chris <30196510+systemcatch@users.noreply.github.com> Date: Sun, 26 Jun 2022 00:25:28 +0100 Subject: [PATCH 23/44] Bump version to 1.2.3 and update CHANGELOG (#1116) --- CHANGELOG.rst | 7 +++++++ arrow/_version.py | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 12bc86a6..3bf23e02 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,13 @@ Changelog ========= +1.2.3 (2022-06-25) +------------------ + +- [NEW] Added Amharic, Armenian, Georgian, Laotian and Uzbek locales. +- [FIX] Updated Danish locale and associated tests. +- [INTERNAl] Small fixes to CI. + 1.2.2 (2022-01-19) ------------------ diff --git a/arrow/_version.py b/arrow/_version.py index bc86c944..10aa336c 100644 --- a/arrow/_version.py +++ b/arrow/_version.py @@ -1 +1 @@ -__version__ = "1.2.2" +__version__ = "1.2.3" From cb03dd32e6625c6d7dc44e267ea7f0e2c1582337 Mon Sep 17 00:00:00 2001 From: "Kristijan \"Fremen\" Velkovski" Date: Tue, 19 Jul 2022 17:32:19 -0500 Subject: [PATCH 24/44] Update Github CI Actions. --- .github/workflows/continuous_integration.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/continuous_integration.yml b/.github/workflows/continuous_integration.yml index d60e4bd3..1ab9b5d1 100644 --- a/.github/workflows/continuous_integration.yml +++ b/.github/workflows/continuous_integration.yml @@ -29,15 +29,15 @@ jobs: - os: windows-latest path: ~\AppData\Local\pip\Cache steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Cache pip - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: ${{ matrix.path }} key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements*.txt') }} restore-keys: ${{ runner.os }}-pip- - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v3 with: python-version: ${{ matrix.python-version }} - name: Install dependencies @@ -47,26 +47,26 @@ jobs: - name: Test with tox run: tox - name: Upload coverage to Codecov - uses: codecov/codecov-action@v2 + uses: codecov/codecov-action@v3 with: file: coverage.xml lint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Python 3.10 - uses: actions/setup-python@v2 + uses: actions/setup-python@v3 with: python-version: "3.10" - name: Cache pip - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: ~/.cache/pip key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements*.txt') }} restore-keys: ${{ runner.os }}-pip- - name: Cache pre-commit - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: ~/.cache/pre-commit key: ${{ runner.os }}-pre-commit-${{ hashFiles('**/.pre-commit-config.yaml') }} From aa862642ab7bf496a503351e6903401a6954a939 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henri=20Niemel=C3=A4inen?= Date: Fri, 22 Jul 2022 05:04:11 +0300 Subject: [PATCH 25/44] Update Finnish locale Use the correct declension for singular second and day, and remove the "muutama" from the plural seconds (seems to be old mishap with localization). --- arrow/locales.py | 6 +++--- tests/test_locales.py | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/arrow/locales.py b/arrow/locales.py index 3627497f..ef7a8edd 100644 --- a/arrow/locales.py +++ b/arrow/locales.py @@ -867,13 +867,13 @@ class FinnishLocale(Locale): timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]] = { "now": "juuri nyt", - "second": "sekunti", - "seconds": {"past": "{0} muutama sekunti", "future": "{0} muutaman sekunnin"}, + "second": {"past": "sekunti", "future": "sekunnin"}, + "seconds": {"past": "{0} sekuntia", "future": "{0} sekunnin"}, "minute": {"past": "minuutti", "future": "minuutin"}, "minutes": {"past": "{0} minuuttia", "future": "{0} minuutin"}, "hour": {"past": "tunti", "future": "tunnin"}, "hours": {"past": "{0} tuntia", "future": "{0} tunnin"}, - "day": "päivä", + "day": {"past": "päivä", "future": "päivän"}, "days": {"past": "{0} päivää", "future": "{0} päivän"}, "month": {"past": "kuukausi", "future": "kuukauden"}, "months": {"past": "{0} kuukautta", "future": "{0} kuukauden"}, diff --git a/tests/test_locales.py b/tests/test_locales.py index 5ff00c1b..099f6f67 100644 --- a/tests/test_locales.py +++ b/tests/test_locales.py @@ -1111,9 +1111,9 @@ def test_format_timeframe(self): # Second(s) assert self.locale._format_timeframe("second", -1) == "sekunti" - assert self.locale._format_timeframe("second", 1) == "sekunti" - assert self.locale._format_timeframe("seconds", -2) == "2 muutama sekunti" - assert self.locale._format_timeframe("seconds", 2) == "2 muutaman sekunnin" + assert self.locale._format_timeframe("second", 1) == "sekunnin" + assert self.locale._format_timeframe("seconds", -2) == "2 sekuntia" + assert self.locale._format_timeframe("seconds", 2) == "2 sekunnin" # Minute(s) assert self.locale._format_timeframe("minute", -1) == "minuutti" @@ -1129,7 +1129,7 @@ def test_format_timeframe(self): # Day(s) assert self.locale._format_timeframe("day", -1) == "päivä" - assert self.locale._format_timeframe("day", 1) == "päivä" + assert self.locale._format_timeframe("day", 1) == "päivän" assert self.locale._format_timeframe("days", -2) == "2 päivää" assert self.locale._format_timeframe("days", 2) == "2 päivän" From 4eb070fd858dc4d167748e498d3be7b0ea01f1cc Mon Sep 17 00:00:00 2001 From: karsazoltan <61280910+karsazoltan@users.noreply.github.com> Date: Mon, 29 Aug 2022 22:28:47 +0200 Subject: [PATCH 26/44] Hungarian Locale Update (#1123) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Karsa Zoltán --- arrow/locales.py | 2 ++ tests/test_locales.py | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/arrow/locales.py b/arrow/locales.py index ef7a8edd..f0d4bc19 100644 --- a/arrow/locales.py +++ b/arrow/locales.py @@ -3709,6 +3709,8 @@ class HungarianLocale(Locale): "hours": {"past": "{0} órával", "future": "{0} óra"}, "day": {"past": "egy nappal", "future": "egy nap"}, "days": {"past": "{0} nappal", "future": "{0} nap"}, + "week": {"past": "egy héttel", "future": "egy hét"}, + "weeks": {"past": "{0} héttel", "future": "{0} hét"}, "month": {"past": "egy hónappal", "future": "egy hónap"}, "months": {"past": "{0} hónappal", "future": "{0} hónap"}, "year": {"past": "egy évvel", "future": "egy év"}, diff --git a/tests/test_locales.py b/tests/test_locales.py index 099f6f67..bef91d74 100644 --- a/tests/test_locales.py +++ b/tests/test_locales.py @@ -1269,6 +1269,12 @@ def test_format_timeframe(self): assert self.locale._format_timeframe("days", -2) == "2 nappal" assert self.locale._format_timeframe("days", 2) == "2 nap" + # Week(s) + assert self.locale._format_timeframe("week", -1) == "egy héttel" + assert self.locale._format_timeframe("week", 1) == "egy hét" + assert self.locale._format_timeframe("weeks", -2) == "2 héttel" + assert self.locale._format_timeframe("weeks", 2) == "2 hét" + # Month(s) assert self.locale._format_timeframe("month", -1) == "egy hónappal" assert self.locale._format_timeframe("month", 1) == "egy hónap" From 5f9dfbef9e5db15266205f0799d17ac8d0a79004 Mon Sep 17 00:00:00 2001 From: Konrad Weihmann <46938494+priv-kweihmann@users.noreply.github.com> Date: Tue, 30 Aug 2022 04:46:29 +0200 Subject: [PATCH 27/44] Parser: Allow UTC prefix in TzInfoParser (#1099) --- arrow/parser.py | 2 +- tests/test_parser.py | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/arrow/parser.py b/arrow/parser.py index e95d78b0..6bf2fba2 100644 --- a/arrow/parser.py +++ b/arrow/parser.py @@ -740,7 +740,7 @@ def _generate_choice_re( class TzinfoParser: _TZINFO_RE: ClassVar[Pattern[str]] = re.compile( - r"^([\+\-])?(\d{2})(?:\:?(\d{2}))?$" + r"^(?:\(UTC)*([\+\-])?(\d{2})(?:\:?(\d{2}))?" ) @classmethod diff --git a/tests/test_parser.py b/tests/test_parser.py index bb4ab148..e92d30c1 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -1367,6 +1367,14 @@ def test_parse_utc(self): assert self.parser.parse("utc") == tz.tzutc() assert self.parser.parse("UTC") == tz.tzutc() + def test_parse_utc_withoffset(self): + assert self.parser.parse("(UTC+01:00") == tz.tzoffset(None, 3600) + assert self.parser.parse("(UTC-01:00") == tz.tzoffset(None, -3600) + assert self.parser.parse("(UTC+01:00") == tz.tzoffset(None, 3600) + assert self.parser.parse( + "(UTC+01:00) Amsterdam, Berlin, Bern, Rom, Stockholm, Wien" + ) == tz.tzoffset(None, 3600) + def test_parse_iso(self): assert self.parser.parse("01:00") == tz.tzoffset(None, 3600) From f8f306848f42742cf771bba2cac5735238e6dcae Mon Sep 17 00:00:00 2001 From: Marc Sommerhalder Date: Wed, 31 Aug 2022 20:27:31 +0200 Subject: [PATCH 28/44] Typo Fix in Italian locale, update Romansh locale (#1121) --- arrow/locales.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/arrow/locales.py b/arrow/locales.py index f0d4bc19..ec6af726 100644 --- a/arrow/locales.py +++ b/arrow/locales.py @@ -425,7 +425,7 @@ class ItalianLocale(Locale): "hours": "{0} ore", "day": "un giorno", "days": "{0} giorni", - "week": "una settimana,", + "week": "una settimana", "weeks": "{0} settimane", "month": "un mese", "months": "{0} mesi", @@ -4139,6 +4139,8 @@ class RomanshLocale(Locale): "hours": "{0} ura", "day": "in di", "days": "{0} dis", + "week": "in'emna", + "weeks": "{0} emnas", "month": "in mais", "months": "{0} mais", "year": "in onn", From 11712752c0829b2d8d27c40009923de8245f1c54 Mon Sep 17 00:00:00 2001 From: gruebel Date: Sat, 1 Oct 2022 19:11:13 +0200 Subject: [PATCH 29/44] add support for Python 3.11 --- .github/workflows/continuous_integration.yml | 2 +- setup.py | 1 + tox.ini | 3 ++- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/continuous_integration.yml b/.github/workflows/continuous_integration.yml index 1ab9b5d1..1f9d5547 100644 --- a/.github/workflows/continuous_integration.yml +++ b/.github/workflows/continuous_integration.yml @@ -15,7 +15,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["pypy-3.7", "3.6", "3.7", "3.8", "3.9", "3.10"] + python-version: ["pypy-3.7", "3.6", "3.7", "3.8", "3.9", "3.10", "3.11-dev"] os: [ubuntu-latest, macos-latest, windows-latest] exclude: # pypy3 randomly fails on Windows builds diff --git a/setup.py b/setup.py index 350a5a0f..52563cf9 100644 --- a/setup.py +++ b/setup.py @@ -38,6 +38,7 @@ "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", ], keywords="arrow date time datetime timestamp timezone humanize", project_urls={ diff --git a/tox.ini b/tox.ini index c51432a2..fcd2d9c1 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,6 @@ [tox] minversion = 3.18.0 -envlist = py{py3,36,37,38,39,310} +envlist = py{py3,36,37,38,39,310,311} skip_missing_interpreters = true [gh-actions] @@ -11,6 +11,7 @@ python = 3.8: py38 3.9: py39 3.10: py310 + 3.11-dev: py311 [testenv] deps = -r requirements/requirements-tests.txt From 48520e7352b4ae290cc0ed64ada8025e1bac1f0b Mon Sep 17 00:00:00 2001 From: gruebel Date: Sun, 2 Oct 2022 10:51:12 +0200 Subject: [PATCH 30/44] adjust python version name in tox.ini --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index fcd2d9c1..11d70cb2 100644 --- a/tox.ini +++ b/tox.ini @@ -11,7 +11,7 @@ python = 3.8: py38 3.9: py39 3.10: py310 - 3.11-dev: py311 + 3.11: py311 [testenv] deps = -r requirements/requirements-tests.txt From 9fcdd6f3437d97c5b3194c9a3cef342a775b541d Mon Sep 17 00:00:00 2001 From: gruebel Date: Sat, 8 Oct 2022 21:42:02 +0200 Subject: [PATCH 31/44] upgrade pre-commit hooks and GHA versions --- .github/workflows/continuous_integration.yml | 4 ++-- .pre-commit-config.yaml | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/continuous_integration.yml b/.github/workflows/continuous_integration.yml index 1f9d5547..21b5417c 100644 --- a/.github/workflows/continuous_integration.yml +++ b/.github/workflows/continuous_integration.yml @@ -37,7 +37,7 @@ jobs: key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements*.txt') }} restore-keys: ${{ runner.os }}-pip- - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v3 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - name: Install dependencies @@ -56,7 +56,7 @@ jobs: steps: - uses: actions/checkout@v3 - name: Set up Python 3.10 - uses: actions/setup-python@v3 + uses: actions/setup-python@v4 with: python-version: "3.10" - name: Cache pip diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b352b70b..2847af8b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,7 +2,7 @@ default_language_version: python: python3 repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.2.0 + rev: v4.3.0 hooks: - id: check-ast - id: check-yaml @@ -22,7 +22,7 @@ repos: hooks: - id: isort - repo: https://github.com/asottile/pyupgrade - rev: v2.32.0 + rev: v3.0.0 hooks: - id: pyupgrade args: [--py36-plus] @@ -38,12 +38,12 @@ repos: - id: rst-inline-touching-normal - id: text-unicode-replacement-char - repo: https://github.com/psf/black - rev: 22.3.0 + rev: 22.8.0 hooks: - id: black args: [--safe, --quiet, --target-version=py36] - repo: https://github.com/pycqa/flake8 - rev: 4.0.1 + rev: 5.0.4 hooks: - id: flake8 additional_dependencies: [flake8-bugbear,flake8-annotations] From 054749e00a71720fc320538b7a8e333a1598001a Mon Sep 17 00:00:00 2001 From: gruebel Date: Fri, 7 Oct 2022 21:16:22 +0200 Subject: [PATCH 32/44] upgrade mypy version --- .pre-commit-config.yaml | 2 +- arrow/locales.py | 1 + tests/test_locales.py | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b352b70b..457e509e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -48,7 +48,7 @@ repos: - id: flake8 additional_dependencies: [flake8-bugbear,flake8-annotations] - repo: https://github.com/pre-commit/mirrors-mypy - rev: 'v0.950' + rev: v0.982 hooks: - id: mypy additional_dependencies: [types-python-dateutil] diff --git a/arrow/locales.py b/arrow/locales.py index ec6af726..ea4c84b2 100644 --- a/arrow/locales.py +++ b/arrow/locales.py @@ -4121,6 +4121,7 @@ def _ordinal_number(self, n: int) -> str: return f"{n}র্থ" if n == 6: return f"{n}ষ্ঠ" + return "" class RomanshLocale(Locale): diff --git a/tests/test_locales.py b/tests/test_locales.py index bef91d74..8312a939 100644 --- a/tests/test_locales.py +++ b/tests/test_locales.py @@ -1394,7 +1394,7 @@ def test_ordinal_number(self): assert self.locale._ordinal_number(10) == "10ম" assert self.locale._ordinal_number(11) == "11তম" assert self.locale._ordinal_number(42) == "42তম" - assert self.locale._ordinal_number(-1) is None + assert self.locale._ordinal_number(-1) == "" @pytest.mark.usefixtures("lang_locale") From d2ceb53279612c4696effe20e6a90f7389e90ed6 Mon Sep 17 00:00:00 2001 From: gruebel Date: Fri, 7 Oct 2022 21:17:46 +0200 Subject: [PATCH 33/44] add error codes to type ignore comments --- arrow/arrow.py | 6 +++--- arrow/parser.py | 6 +++--- setup.cfg | 3 +++ 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/arrow/arrow.py b/arrow/arrow.py index 1ede107f..2e1d977c 100644 --- a/arrow/arrow.py +++ b/arrow/arrow.py @@ -495,7 +495,7 @@ def range( yield current values = [getattr(current, f) for f in cls._ATTRS] - current = cls(*values, tzinfo=tzinfo).shift( # type: ignore + current = cls(*values, tzinfo=tzinfo).shift( # type: ignore[misc] **{frame_relative: relative_steps} ) @@ -578,7 +578,7 @@ def span( for _ in range(3 - len(values)): values.append(1) - floor = self.__class__(*values, tzinfo=self.tzinfo) # type: ignore + floor = self.__class__(*values, tzinfo=self.tzinfo) # type: ignore[misc] if frame_absolute == "week": # if week_start is greater than self.isoweekday() go back one week by setting delta = 7 @@ -1259,7 +1259,7 @@ def humanize( ) if trunc(abs(delta)) != 1: - granularity += "s" # type: ignore + granularity += "s" # type: ignore[assignment] return locale.describe(granularity, delta, only_distance=only_distance) else: diff --git a/arrow/parser.py b/arrow/parser.py index 6bf2fba2..ee31a5bc 100644 --- a/arrow/parser.py +++ b/arrow/parser.py @@ -187,7 +187,7 @@ def __init__(self, locale: str = DEFAULT_LOCALE, cache_size: int = 0) -> None: } ) if cache_size > 0: - self._generate_pattern_re = lru_cache(maxsize=cache_size)( # type: ignore + self._generate_pattern_re = lru_cache(maxsize=cache_size)( # type: ignore[assignment] self._generate_pattern_re ) @@ -341,7 +341,7 @@ def parse( f"Unable to find a match group for the specified token {token!r}." ) - self._parse_token(token, value, parts) # type: ignore + self._parse_token(token, value, parts) # type: ignore[arg-type] return self._build_datetime(parts) @@ -508,7 +508,7 @@ def _parse_token( elif token in ["MMMM", "MMM"]: # FIXME: month_number() is nullable - parts["month"] = self.locale.month_number(value.lower()) # type: ignore + parts["month"] = self.locale.month_number(value.lower()) # type: ignore[typeddict-item] elif token in ["MM", "M"]: parts["month"] = int(value) diff --git a/setup.cfg b/setup.cfg index 3add2419..6ffbd02b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,9 @@ [mypy] python_version = 3.6 +show_error_codes = True +pretty = True + allow_any_expr = True allow_any_decorated = True allow_any_explicit = True From 9c5f0ce28fff47468b7de982bc748ac23cd790c1 Mon Sep 17 00:00:00 2001 From: erwinmintiens Date: Thu, 13 Oct 2022 13:34:55 +0200 Subject: [PATCH 34/44] Added tests for DutchLocale --- tests/test_locales.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/test_locales.py b/tests/test_locales.py index bef91d74..0a7f9137 100644 --- a/tests/test_locales.py +++ b/tests/test_locales.py @@ -2439,6 +2439,26 @@ def test_ordinal_number(self): assert self.locale.ordinal_number(100) == "100번째" +@pytest.mark.usefixtures("lang_locale") +class TestDutchLocale: + def test_plurals(self): + assert self.locale._format_timeframe("now", 0) == "nu" + assert self.locale._format_timeframe("second", 1) == "een seconde" + assert self.locale._format_timeframe("seconds", 30) == "30 seconden" + assert self.locale._format_timeframe("minute", 1) == "een minuut" + assert self.locale._format_timeframe("minutes", 40) == "40 minuten" + assert self.locale._format_timeframe("hour", 1) == "een uur" + assert self.locale._format_timeframe("hours", 23) == "23 uur" + assert self.locale._format_timeframe("day", 1) == "een dag" + assert self.locale._format_timeframe("days", 12) == "12 dagen" + assert self.locale._format_timeframe("week", 1) == "een week" + assert self.locale._format_timeframe("weeks", 38) == "38 weken" + assert self.locale._format_timeframe("month", 1) == "een maand" + assert self.locale._format_timeframe("months", 11) == "11 maanden" + assert self.locale._format_timeframe("year", 1) == "een jaar" + assert self.locale._format_timeframe("years", 12) == "12 jaar" + + @pytest.mark.usefixtures("lang_locale") class TestJapaneseLocale: def test_format_timeframe(self): From 8cac5fabf588b19818f21c0f90c55b851b8fefe6 Mon Sep 17 00:00:00 2001 From: Josh Soref <2119212+jsoref@users.noreply.github.com> Date: Tue, 18 Oct 2022 19:35:10 -0400 Subject: [PATCH 35/44] Spelling Fixes (#1131) --- CHANGELOG.rst | 4 ++-- arrow/arrow.py | 4 ++-- tests/test_arrow.py | 4 ++-- tests/test_parser.py | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 3bf23e02..5b079798 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -285,7 +285,7 @@ After 8 years we're pleased to announce Arrow v1.0. Thanks to the entire Python - [FIX] Consolidated and simplified German locales. - [INTERNAL] Moved testing suite from nosetest/Chai to pytest/pytest-mock. - [INTERNAL] Converted xunit-style setup and teardown functions in tests to pytest fixtures. -- [INTERNAL] Setup Github Actions for CI alongside Travis. +- [INTERNAL] Setup GitHub Actions for CI alongside Travis. - [INTERNAL] Help support Arrow's future development by donating to the project on `Open Collective `_. 0.15.5 (2020-01-03) @@ -659,7 +659,7 @@ The following will work in v0.15.0: - [NEW] Brazilian locale (Augusto2112) - [NEW] Dutch locale (OrangeTux) - [NEW] Italian locale (Pertux) -- [NEW] Austrain locale (LeChewbacca) +- [NEW] Austrian locale (LeChewbacca) - [NEW] Tagalog locale (Marksteve) - [FIX] Corrected spelling and day numbers in German locale (LeChewbacca) - [FIX] Factory ``get`` method should now handle unicode strings correctly (Bwells) diff --git a/arrow/arrow.py b/arrow/arrow.py index 1ede107f..9802a910 100644 --- a/arrow/arrow.py +++ b/arrow/arrow.py @@ -1314,7 +1314,7 @@ def gather_timeframes(_delta: float, _frame: TimeFrameLiteral) -> float: def dehumanize(self, input_string: str, locale: str = "en_us") -> "Arrow": """Returns a new :class:`Arrow ` object, that represents - the time difference relative to the attrbiutes of the + the time difference relative to the attributes of the :class:`Arrow ` object. :param timestring: a ``str`` representing a humanized relative time. @@ -1419,7 +1419,7 @@ def dehumanize(self, input_string: str, locale: str = "en_us") -> "Arrow": # Assert error if string does not modify any units if not any([True for k, v in unit_visited.items() if v]): raise ValueError( - "Input string not valid. Note: Some locales do not support the week granulairty in Arrow. " + "Input string not valid. Note: Some locales do not support the week granularity in Arrow. " "If you are attempting to use the week granularity on an unsupported locale, this could be the cause of this error." ) diff --git a/tests/test_arrow.py b/tests/test_arrow.py index 38d5000a..5cd12c82 100644 --- a/tests/test_arrow.py +++ b/tests/test_arrow.py @@ -2295,8 +2295,8 @@ def test_empty_granularity_list(self): arw.humanize(later, granularity=[]) # Bulgarian is an example of a language that overrides _format_timeframe - # Applicabale to all locales. Note: Contributors need to make sure - # that if they override describe or describe_mutli, that delta + # Applicable to all locales. Note: Contributors need to make sure + # that if they override describe or describe_multi, that delta # is truncated on call def test_no_floats(self): diff --git a/tests/test_parser.py b/tests/test_parser.py index e92d30c1..bdcc1026 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -917,9 +917,9 @@ def test_timestamp_milli(self): def test_time(self): time_re = parser.DateTimeParser._TIME_RE - time_seperators = [":", ""] + time_separators = [":", ""] - for sep in time_seperators: + for sep in time_separators: assert time_re.findall("12") == [("12", "", "", "", "")] assert time_re.findall(f"12{sep}35") == [("12", "35", "", "", "")] assert time_re.findall("12{sep}35{sep}46".format(sep=sep)) == [ From e7fa554dc9f80170990bd4621683802b57d73fc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20K=C3=A4ufl?= Date: Tue, 25 Oct 2022 14:37:13 +0200 Subject: [PATCH 36/44] Run tests against Python 3.11 stable --- .github/workflows/continuous_integration.yml | 2 +- Makefile | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/continuous_integration.yml b/.github/workflows/continuous_integration.yml index 21b5417c..34d9c4f2 100644 --- a/.github/workflows/continuous_integration.yml +++ b/.github/workflows/continuous_integration.yml @@ -15,7 +15,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["pypy-3.7", "3.6", "3.7", "3.8", "3.9", "3.10", "3.11-dev"] + python-version: ["pypy-3.7", "3.6", "3.7", "3.8", "3.9", "3.10", "3.11"] os: [ubuntu-latest, macos-latest, windows-latest] exclude: # pypy3 randomly fails on Windows builds diff --git a/Makefile b/Makefile index 5f885157..f55a3dce 100644 --- a/Makefile +++ b/Makefile @@ -7,6 +7,7 @@ build37: PYTHON_VER = python3.7 build38: PYTHON_VER = python3.8 build39: PYTHON_VER = python3.9 build310: PYTHON_VER = python3.10 +build311: PYTHON_VER = python3.11 build36 build37 build38 build39 build310: clean $(PYTHON_VER) -m venv venv From e916a2d55d77b51c1bd7983b1e2bc2c18b4d6b35 Mon Sep 17 00:00:00 2001 From: Haffi Mazhar <53666594+haffi96@users.noreply.github.com> Date: Tue, 1 Nov 2022 18:34:06 +0000 Subject: [PATCH 37/44] Changing Documentation Theme to Improve Readability (#1135) --- docs/api-guide.rst | 28 ++ docs/conf.py | 30 +- docs/getting-started.rst | 9 + docs/guide.rst | 555 +++++++++++++++++++++++++++ docs/index.rst | 586 +---------------------------- docs/releases.rst | 4 + requirements/requirements-docs.txt | 1 + 7 files changed, 626 insertions(+), 587 deletions(-) create mode 100644 docs/api-guide.rst create mode 100644 docs/getting-started.rst create mode 100644 docs/guide.rst diff --git a/docs/api-guide.rst b/docs/api-guide.rst new file mode 100644 index 00000000..3cf4d394 --- /dev/null +++ b/docs/api-guide.rst @@ -0,0 +1,28 @@ +*************************************** +API Guide +*************************************** + +:mod:`arrow.arrow` +===================== + +.. automodule:: arrow.arrow + :members: + +:mod:`arrow.factory` +===================== + +.. automodule:: arrow.factory + :members: + +:mod:`arrow.api` +===================== + +.. automodule:: arrow.api + :members: + +:mod:`arrow.locale` +===================== + +.. automodule:: arrow.locales + :members: + :undoc-members: diff --git a/docs/conf.py b/docs/conf.py index 68800bca..dee71470 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -20,7 +20,11 @@ # -- General configuration --------------------------------------------------- -extensions = ["sphinx.ext.autodoc", "sphinx_autodoc_typehints"] +extensions = [ + "sphinx.ext.autodoc", + "sphinx_autodoc_typehints", + "sphinx_rtd_theme", +] templates_path = [] @@ -34,7 +38,7 @@ # -- Options for HTML output ------------------------------------------------- -html_theme = "alabaster" +html_theme = "sphinx_rtd_theme" html_theme_path = [] html_static_path = [] @@ -42,21 +46,21 @@ html_show_sphinx = False html_show_copyright = True -# https://alabaster.readthedocs.io/en/latest/customization.html -html_theme_options = { - "description": "Arrow is a sensible and human-friendly approach to dates, times and timestamps.", +html_context = { + "display_github": True, "github_user": "arrow-py", "github_repo": "arrow", - "github_banner": True, - "show_related": False, - "show_powered_by": False, - "github_button": True, - "github_type": "star", - "github_count": "true", # must be a string + "github_version": "master/docs/", } -html_sidebars = { - "**": ["about.html", "localtoc.html", "relations.html", "searchbox.html"] +# https://sphinx-rtd-theme.readthedocs.io/en/stable/index.html +html_theme_options = { + "logo_only": False, + "prev_next_buttons_location": "both", + "style_nav_header_background": "grey", + # TOC options + "collapse_navigation": False, + "navigation_depth": 3, } # Generate PDFs with unicode characters diff --git a/docs/getting-started.rst b/docs/getting-started.rst new file mode 100644 index 00000000..6ebd3346 --- /dev/null +++ b/docs/getting-started.rst @@ -0,0 +1,9 @@ +*************************************** +Getting started +*************************************** + +Assuming you have Python already, follow the guidelines below to get started with Arrow. + +.. include:: ../README.rst + :start-after: Quick Start + :end-before: end-inclusion-marker-do-not-remove diff --git a/docs/guide.rst b/docs/guide.rst new file mode 100644 index 00000000..aef0e880 --- /dev/null +++ b/docs/guide.rst @@ -0,0 +1,555 @@ +*************************************** +User’s Guide +*************************************** + + +Creation +~~~~~~~~ + +Get 'now' easily: + +.. code-block:: python + + >>> arrow.utcnow() + + + >>> arrow.now() + + + >>> arrow.now('US/Pacific') + + +Create from timestamps (:code:`int` or :code:`float`): + +.. code-block:: python + + >>> arrow.get(1367900664) + + + >>> arrow.get(1367900664.152325) + + +Use a naive or timezone-aware datetime, or flexibly specify a timezone: + +.. code-block:: python + + >>> arrow.get(datetime.utcnow()) + + + >>> arrow.get(datetime(2013, 5, 5), 'US/Pacific') + + + >>> from dateutil import tz + >>> arrow.get(datetime(2013, 5, 5), tz.gettz('US/Pacific')) + + + >>> arrow.get(datetime.now(tz.gettz('US/Pacific'))) + + +Parse from a string: + +.. code-block:: python + + >>> arrow.get('2013-05-05 12:30:45', 'YYYY-MM-DD HH:mm:ss') + + +Search a date in a string: + +.. code-block:: python + + >>> arrow.get('June was born in May 1980', 'MMMM YYYY') + + +Some ISO 8601 compliant strings are recognized and parsed without a format string: + + >>> arrow.get('2013-09-30T15:34:00.000-07:00') + + +Arrow objects can be instantiated directly too, with the same arguments as a datetime: + +.. code-block:: python + + >>> arrow.get(2013, 5, 5) + + + >>> arrow.Arrow(2013, 5, 5) + + +Properties +~~~~~~~~~~ + +Get a datetime or timestamp representation: + +.. code-block:: python + + >>> a = arrow.utcnow() + >>> a.datetime + datetime.datetime(2013, 5, 7, 4, 38, 15, 447644, tzinfo=tzutc()) + +Get a naive datetime, and tzinfo: + +.. code-block:: python + + >>> a.naive + datetime.datetime(2013, 5, 7, 4, 38, 15, 447644) + + >>> a.tzinfo + tzutc() + +Get any datetime value: + +.. code-block:: python + + >>> a.year + 2013 + +Call datetime functions that return properties: + +.. code-block:: python + + >>> a.date() + datetime.date(2013, 5, 7) + + >>> a.time() + datetime.time(4, 38, 15, 447644) + +Replace & Shift +~~~~~~~~~~~~~~~ + +Get a new :class:`Arrow ` object, with altered attributes, just as you would with a datetime: + +.. code-block:: python + + >>> arw = arrow.utcnow() + >>> arw + + + >>> arw.replace(hour=4, minute=40) + + +Or, get one with attributes shifted forward or backward: + +.. code-block:: python + + >>> arw.shift(weeks=+3) + + +Even replace the timezone without altering other attributes: + +.. code-block:: python + + >>> arw.replace(tzinfo='US/Pacific') + + +Move between the earlier and later moments of an ambiguous time: + +.. code-block:: python + + >>> paris_transition = arrow.Arrow(2019, 10, 27, 2, tzinfo="Europe/Paris", fold=0) + >>> paris_transition + + >>> paris_transition.ambiguous + True + >>> paris_transition.replace(fold=1) + + +Format +~~~~~~ + +.. code-block:: python + + >>> arrow.utcnow().format('YYYY-MM-DD HH:mm:ss ZZ') + '2013-05-07 05:23:16 -00:00' + +Convert +~~~~~~~ + +Convert from UTC to other timezones by name or tzinfo: + +.. code-block:: python + + >>> utc = arrow.utcnow() + >>> utc + + + >>> utc.to('US/Pacific') + + + >>> utc.to(tz.gettz('US/Pacific')) + + +Or using shorthand: + +.. code-block:: python + + >>> utc.to('local') + + + >>> utc.to('local').to('utc') + + + +Humanize +~~~~~~~~ + +Humanize relative to now: + +.. code-block:: python + + >>> past = arrow.utcnow().shift(hours=-1) + >>> past.humanize() + 'an hour ago' + +Or another Arrow, or datetime: + +.. code-block:: python + + >>> present = arrow.utcnow() + >>> future = present.shift(hours=2) + >>> future.humanize(present) + 'in 2 hours' + +Indicate time as relative or include only the distance + +.. code-block:: python + + >>> present = arrow.utcnow() + >>> future = present.shift(hours=2) + >>> future.humanize(present) + 'in 2 hours' + >>> future.humanize(present, only_distance=True) + '2 hours' + + +Indicate a specific time granularity (or multiple): + +.. code-block:: python + + >>> present = arrow.utcnow() + >>> future = present.shift(minutes=66) + >>> future.humanize(present, granularity="minute") + 'in 66 minutes' + >>> future.humanize(present, granularity=["hour", "minute"]) + 'in an hour and 6 minutes' + >>> present.humanize(future, granularity=["hour", "minute"]) + 'an hour and 6 minutes ago' + >>> future.humanize(present, only_distance=True, granularity=["hour", "minute"]) + 'an hour and 6 minutes' + +Support for a growing number of locales (see ``locales.py`` for supported languages): + +.. code-block:: python + + + >>> future = arrow.utcnow().shift(hours=1) + >>> future.humanize(a, locale='ru') + 'через 2 час(а,ов)' + +Dehumanize +~~~~~~~~~~ + +Take a human readable string and use it to shift into a past time: + +.. code-block:: python + + >>> arw = arrow.utcnow() + >>> arw + + >>> earlier = arw.dehumanize("2 days ago") + >>> earlier + + +Or use it to shift into a future time: + +.. code-block:: python + + >>> arw = arrow.utcnow() + >>> arw + + >>> later = arw.dehumanize("in a month") + >>> later + + +Support for a growing number of locales (see ``constants.py`` for supported languages): + +.. code-block:: python + + >>> arw = arrow.utcnow() + >>> arw + + >>> later = arw.dehumanize("एक माह बाद", locale="hi") + >>> later + + +Ranges & Spans +~~~~~~~~~~~~~~ + +Get the time span of any unit: + +.. code-block:: python + + >>> arrow.utcnow().span('hour') + (, ) + +Or just get the floor and ceiling: + +.. code-block:: python + + >>> arrow.utcnow().floor('hour') + + + >>> arrow.utcnow().ceil('hour') + + +You can also get a range of time spans: + +.. code-block:: python + + >>> start = datetime(2013, 5, 5, 12, 30) + >>> end = datetime(2013, 5, 5, 17, 15) + >>> for r in arrow.Arrow.span_range('hour', start, end): + ... print(r) + ... + (, ) + (, ) + (, ) + (, ) + (, ) + +Or just iterate over a range of time: + +.. code-block:: python + + >>> start = datetime(2013, 5, 5, 12, 30) + >>> end = datetime(2013, 5, 5, 17, 15) + >>> for r in arrow.Arrow.range('hour', start, end): + ... print(repr(r)) + ... + + + + + + +.. toctree:: + :maxdepth: 2 + +Factories +~~~~~~~~~ + +Use factories to harness Arrow's module API for a custom Arrow-derived type. First, derive your type: + +.. code-block:: python + + >>> class CustomArrow(arrow.Arrow): + ... + ... def days_till_xmas(self): + ... + ... xmas = arrow.Arrow(self.year, 12, 25) + ... + ... if self > xmas: + ... xmas = xmas.shift(years=1) + ... + ... return (xmas - self).days + + +Then get and use a factory for it: + +.. code-block:: python + + >>> factory = arrow.ArrowFactory(CustomArrow) + >>> custom = factory.utcnow() + >>> custom + >>> + + >>> custom.days_till_xmas() + >>> 211 + +Supported Tokens +~~~~~~~~~~~~~~~~ + +Use the following tokens for parsing and formatting. Note that they are **not** the same as the tokens for `strptime `_: + ++--------------------------------+--------------+-------------------------------------------+ +| |Token |Output | ++================================+==============+===========================================+ +|**Year** |YYYY |2000, 2001, 2002 ... 2012, 2013 | ++--------------------------------+--------------+-------------------------------------------+ +| |YY |00, 01, 02 ... 12, 13 | ++--------------------------------+--------------+-------------------------------------------+ +|**Month** |MMMM |January, February, March ... [#t1]_ | ++--------------------------------+--------------+-------------------------------------------+ +| |MMM |Jan, Feb, Mar ... [#t1]_ | ++--------------------------------+--------------+-------------------------------------------+ +| |MM |01, 02, 03 ... 11, 12 | ++--------------------------------+--------------+-------------------------------------------+ +| |M |1, 2, 3 ... 11, 12 | ++--------------------------------+--------------+-------------------------------------------+ +|**Day of Year** |DDDD |001, 002, 003 ... 364, 365 | ++--------------------------------+--------------+-------------------------------------------+ +| |DDD |1, 2, 3 ... 364, 365 | ++--------------------------------+--------------+-------------------------------------------+ +|**Day of Month** |DD |01, 02, 03 ... 30, 31 | ++--------------------------------+--------------+-------------------------------------------+ +| |D |1, 2, 3 ... 30, 31 | ++--------------------------------+--------------+-------------------------------------------+ +| |Do |1st, 2nd, 3rd ... 30th, 31st | ++--------------------------------+--------------+-------------------------------------------+ +|**Day of Week** |dddd |Monday, Tuesday, Wednesday ... [#t2]_ | ++--------------------------------+--------------+-------------------------------------------+ +| |ddd |Mon, Tue, Wed ... [#t2]_ | ++--------------------------------+--------------+-------------------------------------------+ +| |d |1, 2, 3 ... 6, 7 | ++--------------------------------+--------------+-------------------------------------------+ +|**ISO week date** |W |2011-W05-4, 2019-W17 | ++--------------------------------+--------------+-------------------------------------------+ +|**Hour** |HH |00, 01, 02 ... 23, 24 | ++--------------------------------+--------------+-------------------------------------------+ +| |H |0, 1, 2 ... 23, 24 | ++--------------------------------+--------------+-------------------------------------------+ +| |hh |01, 02, 03 ... 11, 12 | ++--------------------------------+--------------+-------------------------------------------+ +| |h |1, 2, 3 ... 11, 12 | ++--------------------------------+--------------+-------------------------------------------+ +|**AM / PM** |A |AM, PM, am, pm [#t1]_ | ++--------------------------------+--------------+-------------------------------------------+ +| |a |am, pm [#t1]_ | ++--------------------------------+--------------+-------------------------------------------+ +|**Minute** |mm |00, 01, 02 ... 58, 59 | ++--------------------------------+--------------+-------------------------------------------+ +| |m |0, 1, 2 ... 58, 59 | ++--------------------------------+--------------+-------------------------------------------+ +|**Second** |ss |00, 01, 02 ... 58, 59 | ++--------------------------------+--------------+-------------------------------------------+ +| |s |0, 1, 2 ... 58, 59 | ++--------------------------------+--------------+-------------------------------------------+ +|**Sub-second** |S... |0, 02, 003, 000006, 123123123123... [#t3]_ | ++--------------------------------+--------------+-------------------------------------------+ +|**Timezone** |ZZZ |Asia/Baku, Europe/Warsaw, GMT ... [#t4]_ | ++--------------------------------+--------------+-------------------------------------------+ +| |ZZ |-07:00, -06:00 ... +06:00, +07:00, +08, Z | ++--------------------------------+--------------+-------------------------------------------+ +| |Z |-0700, -0600 ... +0600, +0700, +08, Z | ++--------------------------------+--------------+-------------------------------------------+ +|**Seconds Timestamp** |X |1381685817, 1381685817.915482 ... [#t5]_ | ++--------------------------------+--------------+-------------------------------------------+ +|**ms or µs Timestamp** |x |1569980330813, 1569980330813221 | ++--------------------------------+--------------+-------------------------------------------+ + +.. rubric:: Footnotes + +.. [#t1] localization support for parsing and formatting +.. [#t2] localization support only for formatting +.. [#t3] the result is truncated to microseconds, with `half-to-even rounding `_. +.. [#t4] timezone names from `tz database `_ provided via dateutil package, note that abbreviations such as MST, PDT, BRST are unlikely to parse due to ambiguity. Use the full IANA zone name instead (Asia/Shanghai, Europe/London, America/Chicago etc). +.. [#t5] this token cannot be used for parsing timestamps out of natural language strings due to compatibility reasons + +Built-in Formats +++++++++++++++++ + +There are several formatting standards that are provided as built-in tokens. + +.. code-block:: python + + >>> arw = arrow.utcnow() + >>> arw.format(arrow.FORMAT_ATOM) + '2020-05-27 10:30:35+00:00' + >>> arw.format(arrow.FORMAT_COOKIE) + 'Wednesday, 27-May-2020 10:30:35 UTC' + >>> arw.format(arrow.FORMAT_RSS) + 'Wed, 27 May 2020 10:30:35 +0000' + >>> arw.format(arrow.FORMAT_RFC822) + 'Wed, 27 May 20 10:30:35 +0000' + >>> arw.format(arrow.FORMAT_RFC850) + 'Wednesday, 27-May-20 10:30:35 UTC' + >>> arw.format(arrow.FORMAT_RFC1036) + 'Wed, 27 May 20 10:30:35 +0000' + >>> arw.format(arrow.FORMAT_RFC1123) + 'Wed, 27 May 2020 10:30:35 +0000' + >>> arw.format(arrow.FORMAT_RFC2822) + 'Wed, 27 May 2020 10:30:35 +0000' + >>> arw.format(arrow.FORMAT_RFC3339) + '2020-05-27 10:30:35+00:00' + >>> arw.format(arrow.FORMAT_W3C) + '2020-05-27 10:30:35+00:00' + +Escaping Formats +~~~~~~~~~~~~~~~~ + +Tokens, phrases, and regular expressions in a format string can be escaped when parsing and formatting by enclosing them within square brackets. + +Tokens & Phrases +++++++++++++++++ + +Any `token `_ or phrase can be escaped as follows: + +.. code-block:: python + + >>> fmt = "YYYY-MM-DD h [h] m" + >>> arw = arrow.get("2018-03-09 8 h 40", fmt) + + >>> arw.format(fmt) + '2018-03-09 8 h 40' + + >>> fmt = "YYYY-MM-DD h [hello] m" + >>> arw = arrow.get("2018-03-09 8 hello 40", fmt) + + >>> arw.format(fmt) + '2018-03-09 8 hello 40' + + >>> fmt = "YYYY-MM-DD h [hello world] m" + >>> arw = arrow.get("2018-03-09 8 hello world 40", fmt) + + >>> arw.format(fmt) + '2018-03-09 8 hello world 40' + +This can be useful for parsing dates in different locales such as French, in which it is common to format time strings as "8 h 40" rather than "8:40". + +Regular Expressions ++++++++++++++++++++ + +You can also escape regular expressions by enclosing them within square brackets. In the following example, we are using the regular expression :code:`\s+` to match any number of whitespace characters that separate the tokens. This is useful if you do not know the number of spaces between tokens ahead of time (e.g. in log files). + +.. code-block:: python + + >>> fmt = r"ddd[\s+]MMM[\s+]DD[\s+]HH:mm:ss[\s+]YYYY" + >>> arrow.get("Mon Sep 08 16:41:45 2014", fmt) + + + >>> arrow.get("Mon \tSep 08 16:41:45 2014", fmt) + + + >>> arrow.get("Mon Sep 08 16:41:45 2014", fmt) + + +Punctuation +~~~~~~~~~~~ + +Date and time formats may be fenced on either side by one punctuation character from the following list: ``, . ; : ? ! " \` ' [ ] { } ( ) < >`` + +.. code-block:: python + + >>> arrow.get("Cool date: 2019-10-31T09:12:45.123456+04:30.", "YYYY-MM-DDTHH:mm:ss.SZZ") + + + >>> arrow.get("Tomorrow (2019-10-31) is Halloween!", "YYYY-MM-DD") + + + >>> arrow.get("Halloween is on 2019.10.31.", "YYYY.MM.DD") + + + >>> arrow.get("It's Halloween tomorrow (2019-10-31)!", "YYYY-MM-DD") + # Raises exception because there are multiple punctuation marks following the date + +Redundant Whitespace +~~~~~~~~~~~~~~~~~~~~ + +Redundant whitespace characters (spaces, tabs, and newlines) can be normalized automatically by passing in the ``normalize_whitespace`` flag to ``arrow.get``: + +.. code-block:: python + + >>> arrow.get('\t \n 2013-05-05T12:30:45.123456 \t \n', normalize_whitespace=True) + + + >>> arrow.get('2013-05-05 T \n 12:30:45\t123456', 'YYYY-MM-DD T HH:mm:ss S', normalize_whitespace=True) + diff --git a/docs/index.rst b/docs/index.rst index d4f9ec2a..0ad2fdba 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -3,593 +3,31 @@ Arrow: Better dates & times for Python Release v\ |release| (`Installation`_) (`Changelog `_) +`Go to repository `_ + .. include:: ../README.rst :start-after: start-inclusion-marker-do-not-remove :end-before: end-inclusion-marker-do-not-remove -User's Guide ------------- - -Creation -~~~~~~~~ - -Get 'now' easily: - -.. code-block:: python - - >>> arrow.utcnow() - - - >>> arrow.now() - - - >>> arrow.now('US/Pacific') - - -Create from timestamps (:code:`int` or :code:`float`): - -.. code-block:: python - - >>> arrow.get(1367900664) - - - >>> arrow.get(1367900664.152325) - - -Use a naive or timezone-aware datetime, or flexibly specify a timezone: - -.. code-block:: python - - >>> arrow.get(datetime.utcnow()) - - - >>> arrow.get(datetime(2013, 5, 5), 'US/Pacific') - - - >>> from dateutil import tz - >>> arrow.get(datetime(2013, 5, 5), tz.gettz('US/Pacific')) - - - >>> arrow.get(datetime.now(tz.gettz('US/Pacific'))) - - -Parse from a string: - -.. code-block:: python - - >>> arrow.get('2013-05-05 12:30:45', 'YYYY-MM-DD HH:mm:ss') - - -Search a date in a string: - -.. code-block:: python - - >>> arrow.get('June was born in May 1980', 'MMMM YYYY') - - -Some ISO 8601 compliant strings are recognized and parsed without a format string: - - >>> arrow.get('2013-09-30T15:34:00.000-07:00') - - -Arrow objects can be instantiated directly too, with the same arguments as a datetime: - -.. code-block:: python - - >>> arrow.get(2013, 5, 5) - - - >>> arrow.Arrow(2013, 5, 5) - - -Properties -~~~~~~~~~~ - -Get a datetime or timestamp representation: - -.. code-block:: python - - >>> a = arrow.utcnow() - >>> a.datetime - datetime.datetime(2013, 5, 7, 4, 38, 15, 447644, tzinfo=tzutc()) - -Get a naive datetime, and tzinfo: - -.. code-block:: python - - >>> a.naive - datetime.datetime(2013, 5, 7, 4, 38, 15, 447644) - - >>> a.tzinfo - tzutc() - -Get any datetime value: - -.. code-block:: python - - >>> a.year - 2013 - -Call datetime functions that return properties: - -.. code-block:: python - - >>> a.date() - datetime.date(2013, 5, 7) - - >>> a.time() - datetime.time(4, 38, 15, 447644) - -Replace & Shift -~~~~~~~~~~~~~~~ - -Get a new :class:`Arrow ` object, with altered attributes, just as you would with a datetime: - -.. code-block:: python - - >>> arw = arrow.utcnow() - >>> arw - - - >>> arw.replace(hour=4, minute=40) - - -Or, get one with attributes shifted forward or backward: - -.. code-block:: python - - >>> arw.shift(weeks=+3) - - -Even replace the timezone without altering other attributes: - -.. code-block:: python - - >>> arw.replace(tzinfo='US/Pacific') - - -Move between the earlier and later moments of an ambiguous time: - -.. code-block:: python - - >>> paris_transition = arrow.Arrow(2019, 10, 27, 2, tzinfo="Europe/Paris", fold=0) - >>> paris_transition - - >>> paris_transition.ambiguous - True - >>> paris_transition.replace(fold=1) - - -Format -~~~~~~ - -.. code-block:: python - - >>> arrow.utcnow().format('YYYY-MM-DD HH:mm:ss ZZ') - '2013-05-07 05:23:16 -00:00' - -Convert -~~~~~~~ - -Convert from UTC to other timezones by name or tzinfo: - -.. code-block:: python - - >>> utc = arrow.utcnow() - >>> utc - - - >>> utc.to('US/Pacific') - - - >>> utc.to(tz.gettz('US/Pacific')) - - -Or using shorthand: - -.. code-block:: python - - >>> utc.to('local') - - - >>> utc.to('local').to('utc') - - - -Humanize -~~~~~~~~ - -Humanize relative to now: - -.. code-block:: python - - >>> past = arrow.utcnow().shift(hours=-1) - >>> past.humanize() - 'an hour ago' - -Or another Arrow, or datetime: - -.. code-block:: python - - >>> present = arrow.utcnow() - >>> future = present.shift(hours=2) - >>> future.humanize(present) - 'in 2 hours' - -Indicate time as relative or include only the distance - -.. code-block:: python - - >>> present = arrow.utcnow() - >>> future = present.shift(hours=2) - >>> future.humanize(present) - 'in 2 hours' - >>> future.humanize(present, only_distance=True) - '2 hours' - - -Indicate a specific time granularity (or multiple): - -.. code-block:: python - - >>> present = arrow.utcnow() - >>> future = present.shift(minutes=66) - >>> future.humanize(present, granularity="minute") - 'in 66 minutes' - >>> future.humanize(present, granularity=["hour", "minute"]) - 'in an hour and 6 minutes' - >>> present.humanize(future, granularity=["hour", "minute"]) - 'an hour and 6 minutes ago' - >>> future.humanize(present, only_distance=True, granularity=["hour", "minute"]) - 'an hour and 6 minutes' - -Support for a growing number of locales (see ``locales.py`` for supported languages): - -.. code-block:: python - - - >>> future = arrow.utcnow().shift(hours=1) - >>> future.humanize(a, locale='ru') - 'через 2 час(а,ов)' - -Dehumanize -~~~~~~~~~~ - -Take a human readable string and use it to shift into a past time: - -.. code-block:: python - - >>> arw = arrow.utcnow() - >>> arw - - >>> earlier = arw.dehumanize("2 days ago") - >>> earlier - - -Or use it to shift into a future time: - -.. code-block:: python - - >>> arw = arrow.utcnow() - >>> arw - - >>> later = arw.dehumanize("in a month") - >>> later - - -Support for a growing number of locales (see ``constants.py`` for supported languages): - -.. code-block:: python - - >>> arw = arrow.utcnow() - >>> arw - - >>> later = arw.dehumanize("एक माह बाद", locale="hi") - >>> later - - -Ranges & Spans -~~~~~~~~~~~~~~ - -Get the time span of any unit: - -.. code-block:: python - - >>> arrow.utcnow().span('hour') - (, ) - -Or just get the floor and ceiling: - -.. code-block:: python - - >>> arrow.utcnow().floor('hour') - - - >>> arrow.utcnow().ceil('hour') - - -You can also get a range of time spans: - -.. code-block:: python - - >>> start = datetime(2013, 5, 5, 12, 30) - >>> end = datetime(2013, 5, 5, 17, 15) - >>> for r in arrow.Arrow.span_range('hour', start, end): - ... print(r) - ... - (, ) - (, ) - (, ) - (, ) - (, ) - -Or just iterate over a range of time: - -.. code-block:: python - - >>> start = datetime(2013, 5, 5, 12, 30) - >>> end = datetime(2013, 5, 5, 17, 15) - >>> for r in arrow.Arrow.range('hour', start, end): - ... print(repr(r)) - ... - - - - - - .. toctree:: - :maxdepth: 2 - -Factories -~~~~~~~~~ - -Use factories to harness Arrow's module API for a custom Arrow-derived type. First, derive your type: - -.. code-block:: python - - >>> class CustomArrow(arrow.Arrow): - ... - ... def days_till_xmas(self): - ... - ... xmas = arrow.Arrow(self.year, 12, 25) - ... - ... if self > xmas: - ... xmas = xmas.shift(years=1) - ... - ... return (xmas - self).days - - -Then get and use a factory for it: - -.. code-block:: python - - >>> factory = arrow.ArrowFactory(CustomArrow) - >>> custom = factory.utcnow() - >>> custom - >>> - - >>> custom.days_till_xmas() - >>> 211 - -Supported Tokens -~~~~~~~~~~~~~~~~ + :maxdepth: 2 -Use the following tokens for parsing and formatting. Note that they are **not** the same as the tokens for `strptime `_: + getting-started -+--------------------------------+--------------+-------------------------------------------+ -| |Token |Output | -+================================+==============+===========================================+ -|**Year** |YYYY |2000, 2001, 2002 ... 2012, 2013 | -+--------------------------------+--------------+-------------------------------------------+ -| |YY |00, 01, 02 ... 12, 13 | -+--------------------------------+--------------+-------------------------------------------+ -|**Month** |MMMM |January, February, March ... [#t1]_ | -+--------------------------------+--------------+-------------------------------------------+ -| |MMM |Jan, Feb, Mar ... [#t1]_ | -+--------------------------------+--------------+-------------------------------------------+ -| |MM |01, 02, 03 ... 11, 12 | -+--------------------------------+--------------+-------------------------------------------+ -| |M |1, 2, 3 ... 11, 12 | -+--------------------------------+--------------+-------------------------------------------+ -|**Day of Year** |DDDD |001, 002, 003 ... 364, 365 | -+--------------------------------+--------------+-------------------------------------------+ -| |DDD |1, 2, 3 ... 364, 365 | -+--------------------------------+--------------+-------------------------------------------+ -|**Day of Month** |DD |01, 02, 03 ... 30, 31 | -+--------------------------------+--------------+-------------------------------------------+ -| |D |1, 2, 3 ... 30, 31 | -+--------------------------------+--------------+-------------------------------------------+ -| |Do |1st, 2nd, 3rd ... 30th, 31st | -+--------------------------------+--------------+-------------------------------------------+ -|**Day of Week** |dddd |Monday, Tuesday, Wednesday ... [#t2]_ | -+--------------------------------+--------------+-------------------------------------------+ -| |ddd |Mon, Tue, Wed ... [#t2]_ | -+--------------------------------+--------------+-------------------------------------------+ -| |d |1, 2, 3 ... 6, 7 | -+--------------------------------+--------------+-------------------------------------------+ -|**ISO week date** |W |2011-W05-4, 2019-W17 | -+--------------------------------+--------------+-------------------------------------------+ -|**Hour** |HH |00, 01, 02 ... 23, 24 | -+--------------------------------+--------------+-------------------------------------------+ -| |H |0, 1, 2 ... 23, 24 | -+--------------------------------+--------------+-------------------------------------------+ -| |hh |01, 02, 03 ... 11, 12 | -+--------------------------------+--------------+-------------------------------------------+ -| |h |1, 2, 3 ... 11, 12 | -+--------------------------------+--------------+-------------------------------------------+ -|**AM / PM** |A |AM, PM, am, pm [#t1]_ | -+--------------------------------+--------------+-------------------------------------------+ -| |a |am, pm [#t1]_ | -+--------------------------------+--------------+-------------------------------------------+ -|**Minute** |mm |00, 01, 02 ... 58, 59 | -+--------------------------------+--------------+-------------------------------------------+ -| |m |0, 1, 2 ... 58, 59 | -+--------------------------------+--------------+-------------------------------------------+ -|**Second** |ss |00, 01, 02 ... 58, 59 | -+--------------------------------+--------------+-------------------------------------------+ -| |s |0, 1, 2 ... 58, 59 | -+--------------------------------+--------------+-------------------------------------------+ -|**Sub-second** |S... |0, 02, 003, 000006, 123123123123... [#t3]_ | -+--------------------------------+--------------+-------------------------------------------+ -|**Timezone** |ZZZ |Asia/Baku, Europe/Warsaw, GMT ... [#t4]_ | -+--------------------------------+--------------+-------------------------------------------+ -| |ZZ |-07:00, -06:00 ... +06:00, +07:00, +08, Z | -+--------------------------------+--------------+-------------------------------------------+ -| |Z |-0700, -0600 ... +0600, +0700, +08, Z | -+--------------------------------+--------------+-------------------------------------------+ -|**Seconds Timestamp** |X |1381685817, 1381685817.915482 ... [#t5]_ | -+--------------------------------+--------------+-------------------------------------------+ -|**ms or µs Timestamp** |x |1569980330813, 1569980330813221 | -+--------------------------------+--------------+-------------------------------------------+ - -.. rubric:: Footnotes - -.. [#t1] localization support for parsing and formatting -.. [#t2] localization support only for formatting -.. [#t3] the result is truncated to microseconds, with `half-to-even rounding `_. -.. [#t4] timezone names from `tz database `_ provided via dateutil package, note that abbreviations such as MST, PDT, BRST are unlikely to parse due to ambiguity. Use the full IANA zone name instead (Asia/Shanghai, Europe/London, America/Chicago etc). -.. [#t5] this token cannot be used for parsing timestamps out of natural language strings due to compatibility reasons - -Built-in Formats -++++++++++++++++ - -There are several formatting standards that are provided as built-in tokens. - -.. code-block:: python - - >>> arw = arrow.utcnow() - >>> arw.format(arrow.FORMAT_ATOM) - '2020-05-27 10:30:35+00:00' - >>> arw.format(arrow.FORMAT_COOKIE) - 'Wednesday, 27-May-2020 10:30:35 UTC' - >>> arw.format(arrow.FORMAT_RSS) - 'Wed, 27 May 2020 10:30:35 +0000' - >>> arw.format(arrow.FORMAT_RFC822) - 'Wed, 27 May 20 10:30:35 +0000' - >>> arw.format(arrow.FORMAT_RFC850) - 'Wednesday, 27-May-20 10:30:35 UTC' - >>> arw.format(arrow.FORMAT_RFC1036) - 'Wed, 27 May 20 10:30:35 +0000' - >>> arw.format(arrow.FORMAT_RFC1123) - 'Wed, 27 May 2020 10:30:35 +0000' - >>> arw.format(arrow.FORMAT_RFC2822) - 'Wed, 27 May 2020 10:30:35 +0000' - >>> arw.format(arrow.FORMAT_RFC3339) - '2020-05-27 10:30:35+00:00' - >>> arw.format(arrow.FORMAT_W3C) - '2020-05-27 10:30:35+00:00' - -Escaping Formats -~~~~~~~~~~~~~~~~ - -Tokens, phrases, and regular expressions in a format string can be escaped when parsing and formatting by enclosing them within square brackets. - -Tokens & Phrases -++++++++++++++++ - -Any `token `_ or phrase can be escaped as follows: - -.. code-block:: python - - >>> fmt = "YYYY-MM-DD h [h] m" - >>> arw = arrow.get("2018-03-09 8 h 40", fmt) - - >>> arw.format(fmt) - '2018-03-09 8 h 40' - - >>> fmt = "YYYY-MM-DD h [hello] m" - >>> arw = arrow.get("2018-03-09 8 hello 40", fmt) - - >>> arw.format(fmt) - '2018-03-09 8 hello 40' - - >>> fmt = "YYYY-MM-DD h [hello world] m" - >>> arw = arrow.get("2018-03-09 8 hello world 40", fmt) - - >>> arw.format(fmt) - '2018-03-09 8 hello world 40' - -This can be useful for parsing dates in different locales such as French, in which it is common to format time strings as "8 h 40" rather than "8:40". - -Regular Expressions -+++++++++++++++++++ - -You can also escape regular expressions by enclosing them within square brackets. In the following example, we are using the regular expression :code:`\s+` to match any number of whitespace characters that separate the tokens. This is useful if you do not know the number of spaces between tokens ahead of time (e.g. in log files). - -.. code-block:: python - - >>> fmt = r"ddd[\s+]MMM[\s+]DD[\s+]HH:mm:ss[\s+]YYYY" - >>> arrow.get("Mon Sep 08 16:41:45 2014", fmt) - - - >>> arrow.get("Mon \tSep 08 16:41:45 2014", fmt) - - - >>> arrow.get("Mon Sep 08 16:41:45 2014", fmt) - - -Punctuation -~~~~~~~~~~~ - -Date and time formats may be fenced on either side by one punctuation character from the following list: ``, . ; : ? ! " \` ' [ ] { } ( ) < >`` - -.. code-block:: python - - >>> arrow.get("Cool date: 2019-10-31T09:12:45.123456+04:30.", "YYYY-MM-DDTHH:mm:ss.SZZ") - - - >>> arrow.get("Tomorrow (2019-10-31) is Halloween!", "YYYY-MM-DD") - - - >>> arrow.get("Halloween is on 2019.10.31.", "YYYY.MM.DD") - - - >>> arrow.get("It's Halloween tomorrow (2019-10-31)!", "YYYY-MM-DD") - # Raises exception because there are multiple punctuation marks following the date - -Redundant Whitespace -~~~~~~~~~~~~~~~~~~~~ - -Redundant whitespace characters (spaces, tabs, and newlines) can be normalized automatically by passing in the ``normalize_whitespace`` flag to ``arrow.get``: - -.. code-block:: python - - >>> arrow.get('\t \n 2013-05-05T12:30:45.123456 \t \n', normalize_whitespace=True) - - - >>> arrow.get('2013-05-05 T \n 12:30:45\t123456', 'YYYY-MM-DD T HH:mm:ss S', normalize_whitespace=True) - - -API Guide ---------- - -arrow.arrow -~~~~~~~~~~~ - -.. automodule:: arrow.arrow - :members: - -arrow.factory -~~~~~~~~~~~~~ +--------------- -.. automodule:: arrow.factory - :members: +.. toctree:: + :maxdepth: 2 -arrow.api -~~~~~~~~~ + guide -.. automodule:: arrow.api - :members: +--------------- -arrow.locale -~~~~~~~~~~~~ +.. toctree:: + :maxdepth: 2 -.. automodule:: arrow.locales - :members: - :undoc-members: + api-guide -Release History --------------- .. toctree:: diff --git a/docs/releases.rst b/docs/releases.rst index 22e1e59c..ed21b487 100644 --- a/docs/releases.rst +++ b/docs/releases.rst @@ -1,3 +1,7 @@ +*************************************** +Release History +*************************************** + .. _releases: .. include:: ../CHANGELOG.rst diff --git a/requirements/requirements-docs.txt b/requirements/requirements-docs.txt index de59f1a3..abc47b28 100644 --- a/requirements/requirements-docs.txt +++ b/requirements/requirements-docs.txt @@ -3,3 +3,4 @@ doc8 sphinx sphinx-autobuild sphinx-autodoc-typehints +sphinx_rtd_theme From 74a759b88447b6ecd9fd5de610f272c8fb6130a2 Mon Sep 17 00:00:00 2001 From: "Kristijan \"Fremen\" Velkovski" Date: Tue, 15 Nov 2022 10:25:22 -0600 Subject: [PATCH 38/44] Upgrade pre-commit hook versions (#1140) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d13b4529..b035115a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -22,7 +22,7 @@ repos: hooks: - id: isort - repo: https://github.com/asottile/pyupgrade - rev: v3.0.0 + rev: v3.2.0 hooks: - id: pyupgrade args: [--py36-plus] @@ -38,7 +38,7 @@ repos: - id: rst-inline-touching-normal - id: text-unicode-replacement-char - repo: https://github.com/psf/black - rev: 22.8.0 + rev: 22.10.0 hooks: - id: black args: [--safe, --quiet, --target-version=py36] From de0aea98051f9d99f237e7edd27ae3e5c6bf1489 Mon Sep 17 00:00:00 2001 From: Jad Chaar Date: Mon, 11 Sep 2023 22:30:56 -0700 Subject: [PATCH 39/44] Fix failing builds and drop Py36 and 37 support (#1163) --- .github/workflows/continuous_integration.yml | 23 +-- .pre-commit-config.yaml | 14 +- .readthedocs.yaml | 29 +++ LICENSE | 2 +- Makefile | 7 +- arrow/arrow.py | 21 +- arrow/factory.py | 3 - arrow/formatter.py | 4 - arrow/locales.py | 17 +- arrow/parser.py | 16 +- docs/conf.py | 2 +- requirements/requirements-docs.txt | 4 +- requirements/requirements.txt | 1 - setup.py | 10 +- tests/test_arrow.py | 199 ------------------- tests/test_factory.py | 41 ---- tests/test_formatter.py | 13 -- tests/test_locales.py | 42 ---- tests/test_parser.py | 66 ------ tox.ini | 3 +- 20 files changed, 74 insertions(+), 443 deletions(-) create mode 100644 .readthedocs.yaml diff --git a/.github/workflows/continuous_integration.yml b/.github/workflows/continuous_integration.yml index 34d9c4f2..29b8df6d 100644 --- a/.github/workflows/continuous_integration.yml +++ b/.github/workflows/continuous_integration.yml @@ -9,18 +9,18 @@ on: - cron: "0 0 1 * *" jobs: - test: + unit-tests: name: ${{ matrix.os }} (${{ matrix.python-version }}) runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: - python-version: ["pypy-3.7", "3.6", "3.7", "3.8", "3.9", "3.10", "3.11"] + python-version: ["pypy-3.9", "3.8", "3.9", "3.10", "3.11", "3.12-dev"] os: [ubuntu-latest, macos-latest, windows-latest] exclude: # pypy3 randomly fails on Windows builds - os: windows-latest - python-version: "pypy-3.7" + python-version: "pypy-3.9" include: - os: ubuntu-latest path: ~/.cache/pip @@ -51,26 +51,25 @@ jobs: with: file: coverage.xml - lint: + linting: + name: Linting runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - name: Set up Python 3.10 - uses: actions/setup-python@v4 - with: - python-version: "3.10" - - name: Cache pip - uses: actions/cache@v3 + - uses: actions/cache@v3 with: path: ~/.cache/pip key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements*.txt') }} restore-keys: ${{ runner.os }}-pip- - - name: Cache pre-commit - uses: actions/cache@v3 + - uses: actions/cache@v3 with: path: ~/.cache/pre-commit key: ${{ runner.os }}-pre-commit-${{ hashFiles('**/.pre-commit-config.yaml') }} restore-keys: ${{ runner.os }}-pre-commit- + - name: Set up Python ${{ runner.python-version }} + uses: actions/setup-python@v4 + with: + python-version: "3.11" - name: Install dependencies run: | pip install -U pip setuptools wheel diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b035115a..b8a60a2c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,7 +2,7 @@ default_language_version: python: python3 repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.3.0 + rev: v4.4.0 hooks: - id: check-ast - id: check-yaml @@ -18,16 +18,16 @@ repos: args: [requirements/requirements.txt, requirements/requirements-docs.txt, requirements/requirements-tests.txt] - id: trailing-whitespace - repo: https://github.com/timothycrosley/isort - rev: 5.10.1 + rev: 5.12.0 hooks: - id: isort - repo: https://github.com/asottile/pyupgrade - rev: v3.2.0 + rev: v3.10.1 hooks: - id: pyupgrade args: [--py36-plus] - repo: https://github.com/pre-commit/pygrep-hooks - rev: v1.9.0 + rev: v1.10.0 hooks: - id: python-no-eval - id: python-check-blanket-noqa @@ -38,17 +38,17 @@ repos: - id: rst-inline-touching-normal - id: text-unicode-replacement-char - repo: https://github.com/psf/black - rev: 22.10.0 + rev: 23.9.1 hooks: - id: black args: [--safe, --quiet, --target-version=py36] - repo: https://github.com/pycqa/flake8 - rev: 5.0.4 + rev: 6.1.0 hooks: - id: flake8 additional_dependencies: [flake8-bugbear,flake8-annotations] - repo: https://github.com/pre-commit/mirrors-mypy - rev: v0.982 + rev: v1.5.1 hooks: - id: mypy additional_dependencies: [types-python-dateutil] diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 00000000..57f8dd08 --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,29 @@ +# Read the Docs configuration file for Sphinx projects +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +version: 2 + +build: + os: ubuntu-22.04 + tools: + python: "3.11" + +# Build documentation in the "docs/" directory with Sphinx +sphinx: + configuration: docs/conf.py + # You can configure Sphinx to use a different builder, for instance use the dirhtml builder for simpler URLs + # builder: "dirhtml" + # Fail on all warnings to avoid broken references + # fail_on_warning: true + +# Optionally build your docs in additional formats such as PDF and ePub +# formats: +# - pdf +# - epub + +# Optional but recommended, declare the Python requirements required +# to build your documentation +# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html +python: + install: + - requirements: requirements/requirements-docs.txt diff --git a/LICENSE b/LICENSE index 4f9eea5d..ff864f3b 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2021 Chris Smith + Copyright 2023 Chris Smith Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/Makefile b/Makefile index f55a3dce..27d5cbe4 100644 --- a/Makefile +++ b/Makefile @@ -1,15 +1,14 @@ .PHONY: auto test docs clean -auto: build310 +auto: build311 -build36: PYTHON_VER = python3.6 -build37: PYTHON_VER = python3.7 build38: PYTHON_VER = python3.8 build39: PYTHON_VER = python3.9 build310: PYTHON_VER = python3.10 build311: PYTHON_VER = python3.11 +build312: PYTHON_VER = python3.12 -build36 build37 build38 build39 build310: clean +build36 build37 build38 build39 build310 build311 build312: clean $(PYTHON_VER) -m venv venv . venv/bin/activate; \ pip install -U pip setuptools wheel; \ diff --git a/arrow/arrow.py b/arrow/arrow.py index e855eee0..8d329efd 100644 --- a/arrow/arrow.py +++ b/arrow/arrow.py @@ -168,9 +168,9 @@ def __init__( isinstance(tzinfo, dt_tzinfo) and hasattr(tzinfo, "localize") and hasattr(tzinfo, "zone") - and tzinfo.zone # type: ignore[attr-defined] + and tzinfo.zone ): - tzinfo = parser.TzinfoParser.parse(tzinfo.zone) # type: ignore[attr-defined] + tzinfo = parser.TzinfoParser.parse(tzinfo.zone) elif isinstance(tzinfo, str): tzinfo = parser.TzinfoParser.parse(tzinfo) @@ -792,7 +792,6 @@ def __str__(self) -> str: return self._datetime.isoformat() def __format__(self, formatstr: str) -> str: - if len(formatstr) > 0: return self.format(formatstr) @@ -804,7 +803,6 @@ def __hash__(self) -> int: # attributes and properties def __getattr__(self, name: str) -> int: - if name == "week": return self.isocalendar()[1] @@ -965,7 +963,6 @@ def replace(self, **kwargs: Any) -> "Arrow": absolute_kwargs = {} for key, value in kwargs.items(): - if key in self._ATTRS: absolute_kwargs[key] = value elif key in ["week", "quarter"]: @@ -1022,7 +1019,6 @@ def shift(self, **kwargs: Any) -> "Arrow": additional_attrs = ["weeks", "quarters", "weekday"] for key, value in kwargs.items(): - if key in self._ATTRS_PLURAL or key in additional_attrs: relative_kwargs[key] = value else: @@ -1263,7 +1259,6 @@ def humanize( return locale.describe(granularity, delta, only_distance=only_distance) else: - if not granularity: raise ValueError( "Empty granularity list provided. " @@ -1367,7 +1362,6 @@ def dehumanize(self, input_string: str, locale: str = "en_us") -> "Arrow": # Search input string for each time unit within locale for unit, unit_object in locale_obj.timeframes.items(): - # Need to check the type of unit_object to create the correct dictionary if isinstance(unit_object, Mapping): strings_to_search = unit_object @@ -1378,7 +1372,6 @@ def dehumanize(self, input_string: str, locale: str = "en_us") -> "Arrow": # Needs to cycle all through strings as some locales have strings that # could overlap in a regex match, since input validation isn't being performed. for time_delta, time_string in strings_to_search.items(): - # Replace {0} with regex \d representing digits search_string = str(time_string) search_string = search_string.format(r"\d+") @@ -1718,7 +1711,6 @@ def for_json(self) -> str: # math def __add__(self, other: Any) -> "Arrow": - if isinstance(other, (timedelta, relativedelta)): return self.fromdatetime(self._datetime + other, self._datetime.tzinfo) @@ -1736,7 +1728,6 @@ def __sub__(self, other: Union[dt_datetime, "Arrow"]) -> timedelta: pass # pragma: no cover def __sub__(self, other: Any) -> Union[timedelta, "Arrow"]: - if isinstance(other, (timedelta, relativedelta)): return self.fromdatetime(self._datetime - other, self._datetime.tzinfo) @@ -1749,7 +1740,6 @@ def __sub__(self, other: Any) -> Union[timedelta, "Arrow"]: return NotImplemented def __rsub__(self, other: Any) -> timedelta: - if isinstance(other, dt_datetime): return other - self._datetime @@ -1758,42 +1748,36 @@ def __rsub__(self, other: Any) -> timedelta: # comparisons def __eq__(self, other: Any) -> bool: - if not isinstance(other, (Arrow, dt_datetime)): return False return self._datetime == self._get_datetime(other) def __ne__(self, other: Any) -> bool: - if not isinstance(other, (Arrow, dt_datetime)): return True return not self.__eq__(other) def __gt__(self, other: Any) -> bool: - if not isinstance(other, (Arrow, dt_datetime)): return NotImplemented return self._datetime > self._get_datetime(other) def __ge__(self, other: Any) -> bool: - if not isinstance(other, (Arrow, dt_datetime)): return NotImplemented return self._datetime >= self._get_datetime(other) def __lt__(self, other: Any) -> bool: - if not isinstance(other, (Arrow, dt_datetime)): return NotImplemented return self._datetime < self._get_datetime(other) def __le__(self, other: Any) -> bool: - if not isinstance(other, (Arrow, dt_datetime)): return NotImplemented @@ -1865,7 +1849,6 @@ def _get_frames(cls, name: _T_FRAMES) -> Tuple[str, str, int]: def _get_iteration_params(cls, end: Any, limit: Optional[int]) -> Tuple[Any, int]: """Sets default end and limit values for range method.""" if end is None: - if limit is None: raise ValueError("One of 'end' or 'limit' is required.") diff --git a/arrow/factory.py b/arrow/factory.py index aad4af8b..f35085f1 100644 --- a/arrow/factory.py +++ b/arrow/factory.py @@ -267,11 +267,9 @@ def get(self, *args: Any, **kwargs: Any) -> Arrow: raise TypeError(f"Cannot parse single argument of type {type(arg)!r}.") elif arg_count == 2: - arg_1, arg_2 = args[0], args[1] if isinstance(arg_1, datetime): - # (datetime, tzinfo/str) -> fromdatetime @ tzinfo if isinstance(arg_2, (dt_tzinfo, str)): return self.type.fromdatetime(arg_1, tzinfo=arg_2) @@ -281,7 +279,6 @@ def get(self, *args: Any, **kwargs: Any) -> Arrow: ) elif isinstance(arg_1, date): - # (date, tzinfo/str) -> fromdate @ tzinfo if isinstance(arg_2, (dt_tzinfo, str)): return self.type.fromdate(arg_1, tzinfo=arg_2) diff --git a/arrow/formatter.py b/arrow/formatter.py index 728bea1a..d45f7153 100644 --- a/arrow/formatter.py +++ b/arrow/formatter.py @@ -29,7 +29,6 @@ class DateTimeFormatter: - # This pattern matches characters enclosed in square brackets are matched as # an atomic group. For more info on atomic groups and how to they are # emulated in Python's re library, see https://stackoverflow.com/a/13577411/2701578 @@ -41,18 +40,15 @@ class DateTimeFormatter: locale: locales.Locale def __init__(self, locale: str = DEFAULT_LOCALE) -> None: - self.locale = locales.get_locale(locale) def format(cls, dt: datetime, fmt: str) -> str: - # FIXME: _format_token() is nullable return cls._FORMAT_RE.sub( lambda m: cast(str, cls._format_token(dt, m.group(0))), fmt ) def _format_token(self, dt: datetime, token: Optional[str]) -> Optional[str]: - if token and token.startswith("[") and token.endswith("]"): return token[1:-1] diff --git a/arrow/locales.py b/arrow/locales.py index ea4c84b2..b2e7ee03 100644 --- a/arrow/locales.py +++ b/arrow/locales.py @@ -129,7 +129,6 @@ def __init_subclass__(cls, **kwargs: Any) -> None: _locale_map[locale_name.lower().replace("_", "-")] = cls def __init__(self) -> None: - self._month_name_to_ordinal = None def describe( @@ -174,7 +173,7 @@ def describe_multi( # Needed to determine the correct relative string to use timeframe_value = 0 - for _unit_name, unit_value in timeframes: + for _, unit_value in timeframes: if trunc(unit_value) != 0: timeframe_value = trunc(unit_value) break @@ -285,7 +284,6 @@ def _format_relative( timeframe: TimeFrameLiteral, delta: Union[float, int], ) -> str: - if timeframe == "now": return humanized @@ -1887,7 +1885,7 @@ class GermanBaseLocale(Locale): future = "in {0}" and_word = "und" - timeframes = { + timeframes: ClassVar[Dict[TimeFrameLiteral, str]] = { "now": "gerade eben", "second": "einer Sekunde", "seconds": "{0} Sekunden", @@ -1982,7 +1980,9 @@ def describe( return super().describe(timeframe, delta, only_distance) # German uses a different case without 'in' or 'ago' - humanized = self.timeframes_only_distance[timeframe].format(trunc(abs(delta))) + humanized: str = self.timeframes_only_distance[timeframe].format( + trunc(abs(delta)) + ) return humanized @@ -3936,7 +3936,6 @@ def _format_relative( class LaotianLocale(Locale): - names = ["lo", "lo-la"] past = "{0} ກ່ອນຫນ້ານີ້" @@ -5404,7 +5403,7 @@ class LuxembourgishLocale(Locale): future = "an {0}" and_word = "an" - timeframes = { + timeframes: ClassVar[Dict[TimeFrameLiteral, str]] = { "now": "just elo", "second": "enger Sekonn", "seconds": "{0} Sekonnen", @@ -5492,7 +5491,9 @@ def describe( return super().describe(timeframe, delta, only_distance) # Luxembourgish uses a different case without 'in' or 'ago' - humanized = self.timeframes_only_distance[timeframe].format(trunc(abs(delta))) + humanized: str = self.timeframes_only_distance[timeframe].format( + trunc(abs(delta)) + ) return humanized diff --git a/arrow/parser.py b/arrow/parser.py index ee31a5bc..645e3da7 100644 --- a/arrow/parser.py +++ b/arrow/parser.py @@ -159,7 +159,6 @@ class DateTimeParser: _input_re_map: Dict[_FORMAT_TYPE, Pattern[str]] def __init__(self, locale: str = DEFAULT_LOCALE, cache_size: int = 0) -> None: - self.locale = locales.get_locale(locale) self._input_re_map = self._BASE_INPUT_RE_MAP.copy() self._input_re_map.update( @@ -187,7 +186,7 @@ def __init__(self, locale: str = DEFAULT_LOCALE, cache_size: int = 0) -> None: } ) if cache_size > 0: - self._generate_pattern_re = lru_cache(maxsize=cache_size)( # type: ignore[assignment] + self._generate_pattern_re = lru_cache(maxsize=cache_size)( # type: ignore self._generate_pattern_re ) @@ -196,7 +195,6 @@ def __init__(self, locale: str = DEFAULT_LOCALE, cache_size: int = 0) -> None: def parse_iso( self, datetime_string: str, normalize_whitespace: bool = False ) -> datetime: - if normalize_whitespace: datetime_string = re.sub(r"\s+", " ", datetime_string.strip()) @@ -236,13 +234,14 @@ def parse_iso( ] if has_time: - if has_space_divider: date_string, time_string = datetime_string.split(" ", 1) else: date_string, time_string = datetime_string.split("T", 1) - time_parts = re.split(r"[\+\-Z]", time_string, 1, re.IGNORECASE) + time_parts = re.split( + r"[\+\-Z]", time_string, maxsplit=1, flags=re.IGNORECASE + ) time_components: Optional[Match[str]] = self._TIME_RE.match(time_parts[0]) @@ -303,7 +302,6 @@ def parse( fmt: Union[List[str], str], normalize_whitespace: bool = False, ) -> datetime: - if normalize_whitespace: datetime_string = re.sub(r"\s+", " ", datetime_string) @@ -346,7 +344,6 @@ def parse( return self._build_datetime(parts) def _generate_pattern_re(self, fmt: str) -> Tuple[List[_FORMAT_TYPE], Pattern[str]]: - # fmt is a string of tokens like 'YYYY-MM-DD' # we construct a new string by replacing each # token by its pattern: @@ -498,7 +495,6 @@ def _parse_token( value: Any, parts: _Parts, ) -> None: - if token == "YYYY": parts["year"] = int(value) @@ -588,7 +584,6 @@ def _build_datetime(parts: _Parts) -> datetime: weekdate = parts.get("weekdate") if weekdate is not None: - year, week = int(weekdate[0]), int(weekdate[1]) if weekdate[2] is not None: @@ -712,7 +707,6 @@ def _build_datetime(parts: _Parts) -> datetime: ) def _parse_multiformat(self, string: str, formats: Iterable[str]) -> datetime: - _datetime: Optional[datetime] = None for fmt in formats: @@ -745,7 +739,6 @@ class TzinfoParser: @classmethod def parse(cls, tzinfo_string: str) -> dt_tzinfo: - tzinfo: Optional[dt_tzinfo] = None if tzinfo_string == "local": @@ -755,7 +748,6 @@ def parse(cls, tzinfo_string: str) -> dt_tzinfo: tzinfo = tz.tzutc() else: - iso_match = cls._TZINFO_RE.match(tzinfo_string) if iso_match: diff --git a/docs/conf.py b/docs/conf.py index dee71470..aa6fb444 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -13,7 +13,7 @@ # -- Project information ----------------------------------------------------- project = "Arrow 🏹" -copyright = "2021, Chris Smith" +copyright = "2023, Chris Smith" author = "Chris Smith" release = about["__version__"] diff --git a/requirements/requirements-docs.txt b/requirements/requirements-docs.txt index abc47b28..35ca4ded 100644 --- a/requirements/requirements-docs.txt +++ b/requirements/requirements-docs.txt @@ -1,6 +1,6 @@ -r requirements.txt doc8 -sphinx +sphinx>=7.0.0 sphinx-autobuild sphinx-autodoc-typehints -sphinx_rtd_theme +sphinx_rtd_theme>=1.3.0 diff --git a/requirements/requirements.txt b/requirements/requirements.txt index bcdff0e8..65134a19 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -1,2 +1 @@ python-dateutil>=2.7.0 -typing_extensions; python_version < '3.8' diff --git a/setup.py b/setup.py index 52563cf9..a2d8921e 100644 --- a/setup.py +++ b/setup.py @@ -21,11 +21,8 @@ packages=["arrow"], package_data={"arrow": ["py.typed"]}, zip_safe=False, - python_requires=">=3.6", - install_requires=[ - "python-dateutil>=2.7.0", - "typing_extensions; python_version<'3.8'", - ], + python_requires=">=3.8", + install_requires=["python-dateutil>=2.7.0"], classifiers=[ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", @@ -33,12 +30,11 @@ "Topic :: Software Development :: Libraries :: Python Modules", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.6", - "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", ], keywords="arrow date time datetime timestamp timezone humanize", project_urls={ diff --git a/tests/test_arrow.py b/tests/test_arrow.py index 5cd12c82..507c1ab0 100644 --- a/tests/test_arrow.py +++ b/tests/test_arrow.py @@ -18,7 +18,6 @@ class TestTestArrowInit: def test_init_bad_input(self): - with pytest.raises(TypeError): arrow.Arrow(2013) @@ -29,7 +28,6 @@ def test_init_bad_input(self): arrow.Arrow(2013, 2, 2, 12, 30, 45, 9999999) def test_init(self): - result = arrow.Arrow(2013, 2, 2) self.expected = datetime(2013, 2, 2, tzinfo=tz.tzutc()) assert result._datetime == self.expected @@ -60,7 +58,6 @@ def test_init(self): # regression tests for issue #626 def test_init_pytz_timezone(self): - result = arrow.Arrow( 2013, 2, 2, 12, 30, 45, 999999, tzinfo=pytz.timezone("Europe/Paris") ) @@ -84,7 +81,6 @@ def test_init_with_fold(self): class TestTestArrowFactory: def test_now(self): - result = arrow.Arrow.now() assert_datetime_equality( @@ -92,7 +88,6 @@ def test_now(self): ) def test_utcnow(self): - result = arrow.Arrow.utcnow() assert_datetime_equality( @@ -102,7 +97,6 @@ def test_utcnow(self): assert result.fold == 0 def test_fromtimestamp(self): - timestamp = time.time() result = arrow.Arrow.fromtimestamp(timestamp) @@ -126,7 +120,6 @@ def test_fromtimestamp(self): arrow.Arrow.fromtimestamp("invalid timestamp") def test_utcfromtimestamp(self): - timestamp = time.time() result = arrow.Arrow.utcfromtimestamp(timestamp) @@ -138,7 +131,6 @@ def test_utcfromtimestamp(self): arrow.Arrow.utcfromtimestamp("invalid timestamp") def test_fromdatetime(self): - dt = datetime(2013, 2, 3, 12, 30, 45, 1) result = arrow.Arrow.fromdatetime(dt) @@ -146,7 +138,6 @@ def test_fromdatetime(self): assert result._datetime == dt.replace(tzinfo=tz.tzutc()) def test_fromdatetime_dt_tzinfo(self): - dt = datetime(2013, 2, 3, 12, 30, 45, 1, tzinfo=tz.gettz("US/Pacific")) result = arrow.Arrow.fromdatetime(dt) @@ -154,7 +145,6 @@ def test_fromdatetime_dt_tzinfo(self): assert result._datetime == dt.replace(tzinfo=tz.gettz("US/Pacific")) def test_fromdatetime_tzinfo_arg(self): - dt = datetime(2013, 2, 3, 12, 30, 45, 1) result = arrow.Arrow.fromdatetime(dt, tz.gettz("US/Pacific")) @@ -162,7 +152,6 @@ def test_fromdatetime_tzinfo_arg(self): assert result._datetime == dt.replace(tzinfo=tz.gettz("US/Pacific")) def test_fromdate(self): - dt = date(2013, 2, 3) result = arrow.Arrow.fromdate(dt, tz.gettz("US/Pacific")) @@ -170,7 +159,6 @@ def test_fromdate(self): assert result._datetime == datetime(2013, 2, 3, tzinfo=tz.gettz("US/Pacific")) def test_strptime(self): - formatted = datetime(2013, 2, 3, 12, 30, 45).strftime("%Y-%m-%d %H:%M:%S") result = arrow.Arrow.strptime(formatted, "%Y-%m-%d %H:%M:%S") @@ -184,7 +172,6 @@ def test_strptime(self): ) def test_fromordinal(self): - timestamp = 1607066909.937968 with pytest.raises(TypeError): arrow.Arrow.fromordinal(timestamp) @@ -205,43 +192,36 @@ def test_fromordinal(self): @pytest.mark.usefixtures("time_2013_02_03") class TestTestArrowRepresentation: def test_repr(self): - result = self.arrow.__repr__() assert result == f"" def test_str(self): - result = self.arrow.__str__() assert result == self.arrow._datetime.isoformat() def test_hash(self): - result = self.arrow.__hash__() assert result == self.arrow._datetime.__hash__() def test_format(self): - result = f"{self.arrow:YYYY-MM-DD}" assert result == "2013-02-03" def test_bare_format(self): - result = self.arrow.format() assert result == "2013-02-03 12:30:45+00:00" def test_format_no_format_string(self): - result = f"{self.arrow}" assert result == str(self.arrow) def test_clone(self): - result = self.arrow.clone() assert result is not self.arrow @@ -251,12 +231,10 @@ def test_clone(self): @pytest.mark.usefixtures("time_2013_01_01") class TestArrowAttribute: def test_getattr_base(self): - with pytest.raises(AttributeError): self.arrow.prop def test_getattr_week(self): - assert self.arrow.week == 1 def test_getattr_quarter(self): @@ -281,31 +259,24 @@ def test_getattr_quarter(self): assert q4.quarter == 4 def test_getattr_dt_value(self): - assert self.arrow.year == 2013 def test_tzinfo(self): - assert self.arrow.tzinfo == tz.tzutc() def test_naive(self): - assert self.arrow.naive == self.arrow._datetime.replace(tzinfo=None) def test_timestamp(self): - assert self.arrow.timestamp() == self.arrow._datetime.timestamp() def test_int_timestamp(self): - assert self.arrow.int_timestamp == int(self.arrow._datetime.timestamp()) def test_float_timestamp(self): - assert self.arrow.float_timestamp == self.arrow._datetime.timestamp() def test_getattr_fold(self): - # UTC is always unambiguous assert self.now.fold == 0 @@ -318,7 +289,6 @@ def test_getattr_fold(self): ambiguous_dt.fold = 0 def test_getattr_ambiguous(self): - assert not self.now.ambiguous ambiguous_dt = arrow.Arrow(2017, 10, 29, 2, 0, tzinfo="Europe/Stockholm") @@ -326,7 +296,6 @@ def test_getattr_ambiguous(self): assert ambiguous_dt.ambiguous def test_getattr_imaginary(self): - assert not self.now.imaginary imaginary_dt = arrow.Arrow(2013, 3, 31, 2, 30, tzinfo="Europe/Paris") @@ -337,19 +306,16 @@ def test_getattr_imaginary(self): @pytest.mark.usefixtures("time_utcnow") class TestArrowComparison: def test_eq(self): - assert self.arrow == self.arrow assert self.arrow == self.arrow.datetime assert not (self.arrow == "abc") def test_ne(self): - assert not (self.arrow != self.arrow) assert not (self.arrow != self.arrow.datetime) assert self.arrow != "abc" def test_gt(self): - arrow_cmp = self.arrow.shift(minutes=1) assert not (self.arrow > self.arrow) @@ -362,7 +328,6 @@ def test_gt(self): assert self.arrow < arrow_cmp.datetime def test_ge(self): - with pytest.raises(TypeError): self.arrow >= "abc" # noqa: B015 @@ -370,7 +335,6 @@ def test_ge(self): assert self.arrow >= self.arrow.datetime def test_lt(self): - arrow_cmp = self.arrow.shift(minutes=1) assert not (self.arrow < self.arrow) @@ -383,7 +347,6 @@ def test_lt(self): assert self.arrow < arrow_cmp.datetime def test_le(self): - with pytest.raises(TypeError): self.arrow <= "abc" # noqa: B015 @@ -394,53 +357,44 @@ def test_le(self): @pytest.mark.usefixtures("time_2013_01_01") class TestArrowMath: def test_add_timedelta(self): - result = self.arrow.__add__(timedelta(days=1)) assert result._datetime == datetime(2013, 1, 2, tzinfo=tz.tzutc()) def test_add_other(self): - with pytest.raises(TypeError): self.arrow + 1 def test_radd(self): - result = self.arrow.__radd__(timedelta(days=1)) assert result._datetime == datetime(2013, 1, 2, tzinfo=tz.tzutc()) def test_sub_timedelta(self): - result = self.arrow.__sub__(timedelta(days=1)) assert result._datetime == datetime(2012, 12, 31, tzinfo=tz.tzutc()) def test_sub_datetime(self): - result = self.arrow.__sub__(datetime(2012, 12, 21, tzinfo=tz.tzutc())) assert result == timedelta(days=11) def test_sub_arrow(self): - result = self.arrow.__sub__(arrow.Arrow(2012, 12, 21, tzinfo=tz.tzutc())) assert result == timedelta(days=11) def test_sub_other(self): - with pytest.raises(TypeError): self.arrow - object() def test_rsub_datetime(self): - result = self.arrow.__rsub__(datetime(2012, 12, 21, tzinfo=tz.tzutc())) assert result == timedelta(days=-11) def test_rsub_other(self): - with pytest.raises(TypeError): timedelta(days=1) - self.arrow @@ -448,25 +402,21 @@ def test_rsub_other(self): @pytest.mark.usefixtures("time_utcnow") class TestArrowDatetimeInterface: def test_date(self): - result = self.arrow.date() assert result == self.arrow._datetime.date() def test_time(self): - result = self.arrow.time() assert result == self.arrow._datetime.time() def test_timetz(self): - result = self.arrow.timetz() assert result == self.arrow._datetime.timetz() def test_astimezone(self): - other_tz = tz.gettz("US/Pacific") result = self.arrow.astimezone(other_tz) @@ -474,61 +424,51 @@ def test_astimezone(self): assert result == self.arrow._datetime.astimezone(other_tz) def test_utcoffset(self): - result = self.arrow.utcoffset() assert result == self.arrow._datetime.utcoffset() def test_dst(self): - result = self.arrow.dst() assert result == self.arrow._datetime.dst() def test_timetuple(self): - result = self.arrow.timetuple() assert result == self.arrow._datetime.timetuple() def test_utctimetuple(self): - result = self.arrow.utctimetuple() assert result == self.arrow._datetime.utctimetuple() def test_toordinal(self): - result = self.arrow.toordinal() assert result == self.arrow._datetime.toordinal() def test_weekday(self): - result = self.arrow.weekday() assert result == self.arrow._datetime.weekday() def test_isoweekday(self): - result = self.arrow.isoweekday() assert result == self.arrow._datetime.isoweekday() def test_isocalendar(self): - result = self.arrow.isocalendar() assert result == self.arrow._datetime.isocalendar() def test_isoformat(self): - result = self.arrow.isoformat() assert result == self.arrow._datetime.isoformat() def test_isoformat_timespec(self): - result = self.arrow.isoformat(timespec="hours") assert result == self.arrow._datetime.isoformat(timespec="hours") @@ -542,19 +482,16 @@ def test_isoformat_timespec(self): assert result == self.arrow._datetime.isoformat(sep="x", timespec="seconds") def test_simplejson(self): - result = json.dumps({"v": self.arrow.for_json()}, for_json=True) assert json.loads(result)["v"] == self.arrow._datetime.isoformat() def test_ctime(self): - result = self.arrow.ctime() assert result == self.arrow._datetime.ctime() def test_strftime(self): - result = self.arrow.strftime("%Y") assert result == self.arrow._datetime.strftime("%Y") @@ -609,7 +546,6 @@ def test_dst(self): class TestArrowConversion: def test_to(self): - dt_from = datetime.now() arrow_from = arrow.Arrow.fromdatetime(dt_from, tz.gettz("US/Pacific")) @@ -632,7 +568,6 @@ def test_to_amsterdam_then_utc(self): # regression test for #690 def test_to_israel_same_offset(self): - result = arrow.Arrow(2019, 10, 27, 2, 21, 1, tzinfo="+03:00").to("Israel") expected = arrow.Arrow(2019, 10, 27, 1, 21, 1, tzinfo="Israel") @@ -648,7 +583,6 @@ def test_anchorage_dst(self): # issue 476 def test_chicago_fall(self): - result = arrow.Arrow(2017, 11, 5, 2, 1, tzinfo="-05:00").to("America/Chicago") expected = arrow.Arrow(2017, 11, 5, 1, 1, tzinfo="America/Chicago") @@ -656,7 +590,6 @@ def test_chicago_fall(self): assert result.utcoffset() != expected.utcoffset() def test_toronto_gap(self): - before = arrow.Arrow(2011, 3, 13, 6, 30, tzinfo="UTC").to("America/Toronto") after = arrow.Arrow(2011, 3, 13, 7, 30, tzinfo="UTC").to("America/Toronto") @@ -666,7 +599,6 @@ def test_toronto_gap(self): assert before.utcoffset() != after.utcoffset() def test_sydney_gap(self): - before = arrow.Arrow(2012, 10, 6, 15, 30, tzinfo="UTC").to("Australia/Sydney") after = arrow.Arrow(2012, 10, 6, 16, 30, tzinfo="UTC").to("Australia/Sydney") @@ -678,7 +610,6 @@ def test_sydney_gap(self): class TestArrowPickling: def test_pickle_and_unpickle(self): - dt = arrow.Arrow.utcnow() pickled = pickle.dumps(dt) @@ -690,12 +621,10 @@ def test_pickle_and_unpickle(self): class TestArrowReplace: def test_not_attr(self): - with pytest.raises(ValueError): arrow.Arrow.utcnow().replace(abc=1) def test_replace(self): - arw = arrow.Arrow(2013, 5, 5, 12, 30, 45) assert arw.replace(year=2012) == arrow.Arrow(2012, 5, 5, 12, 30, 45) @@ -706,7 +635,6 @@ def test_replace(self): assert arw.replace(second=1) == arrow.Arrow(2013, 5, 5, 12, 30, 1) def test_replace_tzinfo(self): - arw = arrow.Arrow.utcnow().to("US/Eastern") result = arw.replace(tzinfo=tz.gettz("US/Pacific")) @@ -714,7 +642,6 @@ def test_replace_tzinfo(self): assert result == arw.datetime.replace(tzinfo=tz.gettz("US/Pacific")) def test_replace_fold(self): - before = arrow.Arrow(2017, 11, 5, 1, tzinfo="America/New_York") after = before.replace(fold=1) @@ -724,19 +651,16 @@ def test_replace_fold(self): assert before.utcoffset() != after.utcoffset() def test_replace_fold_and_other(self): - arw = arrow.Arrow(2013, 5, 5, 12, 30, 45) assert arw.replace(fold=1, minute=50) == arrow.Arrow(2013, 5, 5, 12, 50, 45) assert arw.replace(minute=50, fold=1) == arrow.Arrow(2013, 5, 5, 12, 50, 45) def test_replace_week(self): - with pytest.raises(ValueError): arrow.Arrow.utcnow().replace(week=1) def test_replace_quarter(self): - with pytest.raises(ValueError): arrow.Arrow.utcnow().replace(quarter=1) @@ -748,14 +672,12 @@ def test_replace_quarter_and_fold(self): arrow.utcnow().replace(quarter=1, fold=1) def test_replace_other_kwargs(self): - with pytest.raises(AttributeError): arrow.utcnow().replace(abc="def") class TestArrowShift: def test_not_attr(self): - now = arrow.Arrow.utcnow() with pytest.raises(ValueError): @@ -765,7 +687,6 @@ def test_not_attr(self): now.shift(week=1) def test_shift(self): - arw = arrow.Arrow(2013, 5, 5, 12, 30, 45) assert arw.shift(years=1) == arrow.Arrow(2014, 5, 5, 12, 30, 45) @@ -822,7 +743,6 @@ def test_shift(self): assert arw.shift(weekday=SU(2)) == arrow.Arrow(2013, 5, 12, 12, 30, 45) def test_shift_negative(self): - arw = arrow.Arrow(2013, 5, 5, 12, 30, 45) assert arw.shift(years=-1) == arrow.Arrow(2012, 5, 5, 12, 30, 45) @@ -858,7 +778,6 @@ def test_shift_negative(self): assert arw.shift(weekday=SU(-2)) == arrow.Arrow(2013, 4, 28, 12, 30, 45) def test_shift_quarters_bug(self): - arw = arrow.Arrow(2013, 5, 5, 12, 30, 45) # The value of the last-read argument was used instead of the ``quarters`` argument. @@ -876,7 +795,6 @@ def test_shift_quarters_bug(self): ) def test_shift_positive_imaginary(self): - # Avoid shifting into imaginary datetimes, take into account DST and other timezone changes. new_york = arrow.Arrow(2017, 3, 12, 1, 30, tzinfo="America/New_York") @@ -907,7 +825,6 @@ def test_shift_positive_imaginary(self): ) def test_shift_negative_imaginary(self): - new_york = arrow.Arrow(2011, 3, 13, 3, 30, tzinfo="America/New_York") assert new_york.shift(hours=-1) == arrow.Arrow( 2011, 3, 13, 3, 30, tzinfo="America/New_York" @@ -951,7 +868,6 @@ def shift_imaginary_seconds(self): class TestArrowRange: def test_year(self): - result = list( arrow.Arrow.range( "year", datetime(2013, 1, 2, 3, 4, 5), datetime(2016, 4, 5, 6, 7, 8) @@ -966,7 +882,6 @@ def test_year(self): ] def test_quarter(self): - result = list( arrow.Arrow.range( "quarter", datetime(2013, 2, 3, 4, 5, 6), datetime(2013, 5, 6, 7, 8, 9) @@ -979,7 +894,6 @@ def test_quarter(self): ] def test_month(self): - result = list( arrow.Arrow.range( "month", datetime(2013, 2, 3, 4, 5, 6), datetime(2013, 5, 6, 7, 8, 9) @@ -994,7 +908,6 @@ def test_month(self): ] def test_week(self): - result = list( arrow.Arrow.range( "week", datetime(2013, 9, 1, 2, 3, 4), datetime(2013, 10, 1, 2, 3, 4) @@ -1010,7 +923,6 @@ def test_week(self): ] def test_day(self): - result = list( arrow.Arrow.range( "day", datetime(2013, 1, 2, 3, 4, 5), datetime(2013, 1, 5, 6, 7, 8) @@ -1025,7 +937,6 @@ def test_day(self): ] def test_hour(self): - result = list( arrow.Arrow.range( "hour", datetime(2013, 1, 2, 3, 4, 5), datetime(2013, 1, 2, 6, 7, 8) @@ -1048,7 +959,6 @@ def test_hour(self): assert result == [arrow.Arrow(2013, 1, 2, 3, 4, 5)] def test_minute(self): - result = list( arrow.Arrow.range( "minute", datetime(2013, 1, 2, 3, 4, 5), datetime(2013, 1, 2, 3, 7, 8) @@ -1063,7 +973,6 @@ def test_minute(self): ] def test_second(self): - result = list( arrow.Arrow.range( "second", datetime(2013, 1, 2, 3, 4, 5), datetime(2013, 1, 2, 3, 4, 8) @@ -1078,7 +987,6 @@ def test_second(self): ] def test_arrow(self): - result = list( arrow.Arrow.range( "day", @@ -1095,7 +1003,6 @@ def test_arrow(self): ] def test_naive_tz(self): - result = arrow.Arrow.range( "year", datetime(2013, 1, 2, 3), datetime(2016, 4, 5, 6), "US/Pacific" ) @@ -1104,7 +1011,6 @@ def test_naive_tz(self): assert r.tzinfo == tz.gettz("US/Pacific") def test_aware_same_tz(self): - result = arrow.Arrow.range( "day", arrow.Arrow(2013, 1, 1, tzinfo=tz.gettz("US/Pacific")), @@ -1115,7 +1021,6 @@ def test_aware_same_tz(self): assert r.tzinfo == tz.gettz("US/Pacific") def test_aware_different_tz(self): - result = arrow.Arrow.range( "day", datetime(2013, 1, 1, tzinfo=tz.gettz("US/Eastern")), @@ -1126,7 +1031,6 @@ def test_aware_different_tz(self): assert r.tzinfo == tz.gettz("US/Eastern") def test_aware_tz(self): - result = arrow.Arrow.range( "day", datetime(2013, 1, 1, tzinfo=tz.gettz("US/Eastern")), @@ -1150,7 +1054,6 @@ def test_imaginary(self): assert len(utc_range) == len(set(utc_range)) def test_unsupported(self): - with pytest.raises(ValueError): next(arrow.Arrow.range("abc", datetime.utcnow(), datetime.utcnow())) @@ -1206,7 +1109,6 @@ def test_range_over_year_maintains_end_date_across_leap_year(self): class TestArrowSpanRange: def test_year(self): - result = list( arrow.Arrow.span_range("year", datetime(2013, 2, 1), datetime(2016, 3, 31)) ) @@ -1231,7 +1133,6 @@ def test_year(self): ] def test_quarter(self): - result = list( arrow.Arrow.span_range( "quarter", datetime(2013, 2, 2), datetime(2013, 5, 15) @@ -1244,7 +1145,6 @@ def test_quarter(self): ] def test_month(self): - result = list( arrow.Arrow.span_range("month", datetime(2013, 1, 2), datetime(2013, 4, 15)) ) @@ -1257,7 +1157,6 @@ def test_month(self): ] def test_week(self): - result = list( arrow.Arrow.span_range("week", datetime(2013, 2, 2), datetime(2013, 2, 28)) ) @@ -1277,7 +1176,6 @@ def test_week(self): ] def test_day(self): - result = list( arrow.Arrow.span_range( "day", datetime(2013, 1, 1, 12), datetime(2013, 1, 4, 12) @@ -1304,7 +1202,6 @@ def test_day(self): ] def test_days(self): - result = list( arrow.Arrow.span_range( "days", datetime(2013, 1, 1, 12), datetime(2013, 1, 4, 12) @@ -1331,7 +1228,6 @@ def test_days(self): ] def test_hour(self): - result = list( arrow.Arrow.span_range( "hour", datetime(2013, 1, 1, 0, 30), datetime(2013, 1, 1, 3, 30) @@ -1368,7 +1264,6 @@ def test_hour(self): ] def test_minute(self): - result = list( arrow.Arrow.span_range( "minute", datetime(2013, 1, 1, 0, 0, 30), datetime(2013, 1, 1, 0, 3, 30) @@ -1395,7 +1290,6 @@ def test_minute(self): ] def test_second(self): - result = list( arrow.Arrow.span_range( "second", datetime(2013, 1, 1), datetime(2013, 1, 1, 0, 0, 3) @@ -1422,7 +1316,6 @@ def test_second(self): ] def test_naive_tz(self): - tzinfo = tz.gettz("US/Pacific") result = arrow.Arrow.span_range( @@ -1434,7 +1327,6 @@ def test_naive_tz(self): assert c.tzinfo == tzinfo def test_aware_same_tz(self): - tzinfo = tz.gettz("US/Pacific") result = arrow.Arrow.span_range( @@ -1448,7 +1340,6 @@ def test_aware_same_tz(self): assert c.tzinfo == tzinfo def test_aware_different_tz(self): - tzinfo1 = tz.gettz("US/Pacific") tzinfo2 = tz.gettz("US/Eastern") @@ -1463,7 +1354,6 @@ def test_aware_different_tz(self): assert c.tzinfo == tzinfo1 def test_aware_tz(self): - result = arrow.Arrow.span_range( "hour", datetime(2013, 1, 1, 0, tzinfo=tz.gettz("US/Eastern")), @@ -1476,7 +1366,6 @@ def test_aware_tz(self): assert c.tzinfo == tz.gettz("US/Central") def test_bounds_param_is_passed(self): - result = list( arrow.Arrow.span_range( "quarter", datetime(2013, 2, 2), datetime(2013, 5, 15), bounds="[]" @@ -1489,7 +1378,6 @@ def test_bounds_param_is_passed(self): ] def test_exact_bound_exclude(self): - result = list( arrow.Arrow.span_range( "hour", @@ -1709,40 +1597,34 @@ def test_exact(self): @pytest.mark.usefixtures("time_2013_02_15") class TestArrowSpan: def test_span_attribute(self): - with pytest.raises(ValueError): self.arrow.span("span") def test_span_year(self): - floor, ceil = self.arrow.span("year") assert floor == datetime(2013, 1, 1, tzinfo=tz.tzutc()) assert ceil == datetime(2013, 12, 31, 23, 59, 59, 999999, tzinfo=tz.tzutc()) def test_span_quarter(self): - floor, ceil = self.arrow.span("quarter") assert floor == datetime(2013, 1, 1, tzinfo=tz.tzutc()) assert ceil == datetime(2013, 3, 31, 23, 59, 59, 999999, tzinfo=tz.tzutc()) def test_span_quarter_count(self): - floor, ceil = self.arrow.span("quarter", 2) assert floor == datetime(2013, 1, 1, tzinfo=tz.tzutc()) assert ceil == datetime(2013, 6, 30, 23, 59, 59, 999999, tzinfo=tz.tzutc()) def test_span_year_count(self): - floor, ceil = self.arrow.span("year", 2) assert floor == datetime(2013, 1, 1, tzinfo=tz.tzutc()) assert ceil == datetime(2014, 12, 31, 23, 59, 59, 999999, tzinfo=tz.tzutc()) def test_span_month(self): - floor, ceil = self.arrow.span("month") assert floor == datetime(2013, 2, 1, tzinfo=tz.tzutc()) @@ -1775,75 +1657,64 @@ def test_span_week(self): assert ceil == datetime(2013, 2, 16, 23, 59, 59, 999999, tzinfo=tz.tzutc()) def test_span_day(self): - floor, ceil = self.arrow.span("day") assert floor == datetime(2013, 2, 15, tzinfo=tz.tzutc()) assert ceil == datetime(2013, 2, 15, 23, 59, 59, 999999, tzinfo=tz.tzutc()) def test_span_hour(self): - floor, ceil = self.arrow.span("hour") assert floor == datetime(2013, 2, 15, 3, tzinfo=tz.tzutc()) assert ceil == datetime(2013, 2, 15, 3, 59, 59, 999999, tzinfo=tz.tzutc()) def test_span_minute(self): - floor, ceil = self.arrow.span("minute") assert floor == datetime(2013, 2, 15, 3, 41, tzinfo=tz.tzutc()) assert ceil == datetime(2013, 2, 15, 3, 41, 59, 999999, tzinfo=tz.tzutc()) def test_span_second(self): - floor, ceil = self.arrow.span("second") assert floor == datetime(2013, 2, 15, 3, 41, 22, tzinfo=tz.tzutc()) assert ceil == datetime(2013, 2, 15, 3, 41, 22, 999999, tzinfo=tz.tzutc()) def test_span_microsecond(self): - floor, ceil = self.arrow.span("microsecond") assert floor == datetime(2013, 2, 15, 3, 41, 22, 8923, tzinfo=tz.tzutc()) assert ceil == datetime(2013, 2, 15, 3, 41, 22, 8923, tzinfo=tz.tzutc()) def test_floor(self): - floor, ceil = self.arrow.span("month") assert floor == self.arrow.floor("month") assert ceil == self.arrow.ceil("month") def test_span_inclusive_inclusive(self): - floor, ceil = self.arrow.span("hour", bounds="[]") assert floor == datetime(2013, 2, 15, 3, tzinfo=tz.tzutc()) assert ceil == datetime(2013, 2, 15, 4, tzinfo=tz.tzutc()) def test_span_exclusive_inclusive(self): - floor, ceil = self.arrow.span("hour", bounds="(]") assert floor == datetime(2013, 2, 15, 3, 0, 0, 1, tzinfo=tz.tzutc()) assert ceil == datetime(2013, 2, 15, 4, tzinfo=tz.tzutc()) def test_span_exclusive_exclusive(self): - floor, ceil = self.arrow.span("hour", bounds="()") assert floor == datetime(2013, 2, 15, 3, 0, 0, 1, tzinfo=tz.tzutc()) assert ceil == datetime(2013, 2, 15, 3, 59, 59, 999999, tzinfo=tz.tzutc()) def test_bounds_are_validated(self): - with pytest.raises(ValueError): floor, ceil = self.arrow.span("hour", bounds="][") def test_exact(self): - result_floor, result_ceil = self.arrow.span("hour", exact=True) expected_floor = datetime(2013, 2, 15, 3, 41, 22, 8923, tzinfo=tz.tzutc()) @@ -1853,28 +1724,24 @@ def test_exact(self): assert result_ceil == expected_ceil def test_exact_inclusive_inclusive(self): - floor, ceil = self.arrow.span("minute", bounds="[]", exact=True) assert floor == datetime(2013, 2, 15, 3, 41, 22, 8923, tzinfo=tz.tzutc()) assert ceil == datetime(2013, 2, 15, 3, 42, 22, 8923, tzinfo=tz.tzutc()) def test_exact_exclusive_inclusive(self): - floor, ceil = self.arrow.span("day", bounds="(]", exact=True) assert floor == datetime(2013, 2, 15, 3, 41, 22, 8924, tzinfo=tz.tzutc()) assert ceil == datetime(2013, 2, 16, 3, 41, 22, 8923, tzinfo=tz.tzutc()) def test_exact_exclusive_exclusive(self): - floor, ceil = self.arrow.span("second", bounds="()", exact=True) assert floor == datetime(2013, 2, 15, 3, 41, 22, 8924, tzinfo=tz.tzutc()) assert ceil == datetime(2013, 2, 15, 3, 41, 23, 8922, tzinfo=tz.tzutc()) def test_all_parameters_specified(self): - floor, ceil = self.arrow.span("week", bounds="()", exact=True, count=2) assert floor == datetime(2013, 2, 15, 3, 41, 22, 8924, tzinfo=tz.tzutc()) @@ -1884,7 +1751,6 @@ def test_all_parameters_specified(self): @pytest.mark.usefixtures("time_2013_01_01") class TestArrowHumanize: def test_granularity(self): - assert self.now.humanize(granularity="second") == "just now" later1 = self.now.shift(seconds=1) @@ -2054,7 +1920,6 @@ def test_multiple_granularity(self): ) def test_seconds(self): - later = self.now.shift(seconds=10) # regression test for issue #727 @@ -2065,7 +1930,6 @@ def test_seconds(self): assert later.humanize(self.now, only_distance=True) == "10 seconds" def test_minute(self): - later = self.now.shift(minutes=1) assert self.now.humanize(later) == "a minute ago" @@ -2075,7 +1939,6 @@ def test_minute(self): assert later.humanize(self.now, only_distance=True) == "a minute" def test_minutes(self): - later = self.now.shift(minutes=2) assert self.now.humanize(later) == "2 minutes ago" @@ -2085,7 +1948,6 @@ def test_minutes(self): assert later.humanize(self.now, only_distance=True) == "2 minutes" def test_hour(self): - later = self.now.shift(hours=1) assert self.now.humanize(later) == "an hour ago" @@ -2095,7 +1957,6 @@ def test_hour(self): assert later.humanize(self.now, only_distance=True) == "an hour" def test_hours(self): - later = self.now.shift(hours=2) assert self.now.humanize(later) == "2 hours ago" @@ -2105,7 +1966,6 @@ def test_hours(self): assert later.humanize(self.now, only_distance=True) == "2 hours" def test_day(self): - later = self.now.shift(days=1) assert self.now.humanize(later) == "a day ago" @@ -2127,7 +1987,6 @@ def test_day(self): assert later.humanize(self.now, only_distance=True) == "a day" def test_days(self): - later = self.now.shift(days=2) assert self.now.humanize(later) == "2 days ago" @@ -2147,7 +2006,6 @@ def test_days(self): assert later.humanize(self.now) == "in 4 days" def test_week(self): - later = self.now.shift(weeks=1) assert self.now.humanize(later) == "a week ago" @@ -2157,7 +2015,6 @@ def test_week(self): assert later.humanize(self.now, only_distance=True) == "a week" def test_weeks(self): - later = self.now.shift(weeks=2) assert self.now.humanize(later) == "2 weeks ago" @@ -2168,7 +2025,6 @@ def test_weeks(self): @pytest.mark.xfail(reason="known issue with humanize month limits") def test_month(self): - later = self.now.shift(months=1) # TODO this test now returns "4 weeks ago", we need to fix this to be correct on a per month basis @@ -2179,7 +2035,6 @@ def test_month(self): assert later.humanize(self.now, only_distance=True) == "a month" def test_month_plus_4_days(self): - # TODO needed for coverage, remove when month limits are fixed later = self.now.shift(months=1, days=4) @@ -2188,7 +2043,6 @@ def test_month_plus_4_days(self): @pytest.mark.xfail(reason="known issue with humanize month limits") def test_months(self): - later = self.now.shift(months=2) earlier = self.now.shift(months=-2) @@ -2199,7 +2053,6 @@ def test_months(self): assert later.humanize(self.now, only_distance=True) == "2 months" def test_year(self): - later = self.now.shift(years=1) assert self.now.humanize(later) == "a year ago" @@ -2209,7 +2062,6 @@ def test_year(self): assert later.humanize(self.now, only_distance=True) == "a year" def test_years(self): - later = self.now.shift(years=2) assert self.now.humanize(later) == "2 years ago" @@ -2225,7 +2077,6 @@ def test_years(self): assert result == "in a year" def test_arrow(self): - arw = arrow.Arrow.fromdatetime(self.datetime) result = arw.humanize(arrow.Arrow.fromdatetime(self.datetime)) @@ -2233,7 +2084,6 @@ def test_arrow(self): assert result == "just now" def test_datetime_tzinfo(self): - arw = arrow.Arrow.fromdatetime(self.datetime) result = arw.humanize(self.datetime.replace(tzinfo=tz.tzutc())) @@ -2241,21 +2091,18 @@ def test_datetime_tzinfo(self): assert result == "just now" def test_other(self): - arw = arrow.Arrow.fromdatetime(self.datetime) with pytest.raises(TypeError): arw.humanize(object()) def test_invalid_locale(self): - arw = arrow.Arrow.fromdatetime(self.datetime) with pytest.raises(ValueError): arw.humanize(locale="klingon") def test_none(self): - arw = arrow.Arrow.utcnow() result = arw.humanize() @@ -2277,7 +2124,6 @@ def test_week_limit(self): assert result == "a week ago" def test_untranslated_granularity(self, mocker): - arw = arrow.Arrow.utcnow() later = arw.shift(weeks=1) @@ -2317,7 +2163,6 @@ def test_no_floats_multi_gran(self): @pytest.mark.usefixtures("time_2013_01_01") class TestArrowHumanizeTestsWithLocale: def test_now(self): - arw = arrow.Arrow(2013, 1, 1, 0, 0, 0) result = arw.humanize(self.datetime, locale="ru") @@ -2331,7 +2176,6 @@ def test_seconds(self): assert result == "через 44 секунды" def test_years(self): - arw = arrow.Arrow(2011, 7, 2) result = arw.humanize(self.datetime, locale="ru") @@ -2582,9 +2426,7 @@ def slavic_locales() -> List[str]: class TestArrowDehumanize: def test_now(self, locale_list_no_weeks: List[str]): - for lang in locale_list_no_weeks: - arw = arrow.Arrow(2000, 6, 18, 5, 55, 0) second_ago = arw.shift(seconds=-1) second_future = arw.shift(seconds=1) @@ -2600,9 +2442,7 @@ def test_now(self, locale_list_no_weeks: List[str]): assert arw.dehumanize(second_future_string, locale=lang) == arw def test_seconds(self, locale_list_no_weeks: List[str]): - for lang in locale_list_no_weeks: - arw = arrow.Arrow(2000, 6, 18, 5, 55, 0) second_ago = arw.shift(seconds=-5) second_future = arw.shift(seconds=5) @@ -2618,9 +2458,7 @@ def test_seconds(self, locale_list_no_weeks: List[str]): assert arw.dehumanize(second_future_string, locale=lang) == second_future def test_minute(self, locale_list_no_weeks: List[str]): - for lang in locale_list_no_weeks: - arw = arrow.Arrow(2001, 6, 18, 5, 55, 0) minute_ago = arw.shift(minutes=-1) minute_future = arw.shift(minutes=1) @@ -2636,9 +2474,7 @@ def test_minute(self, locale_list_no_weeks: List[str]): assert arw.dehumanize(minute_future_string, locale=lang) == minute_future def test_minutes(self, locale_list_no_weeks: List[str]): - for lang in locale_list_no_weeks: - arw = arrow.Arrow(2007, 1, 10, 5, 55, 0) minute_ago = arw.shift(minutes=-5) minute_future = arw.shift(minutes=5) @@ -2654,9 +2490,7 @@ def test_minutes(self, locale_list_no_weeks: List[str]): assert arw.dehumanize(minute_future_string, locale=lang) == minute_future def test_hour(self, locale_list_no_weeks: List[str]): - for lang in locale_list_no_weeks: - arw = arrow.Arrow(2009, 4, 20, 5, 55, 0) hour_ago = arw.shift(hours=-1) hour_future = arw.shift(hours=1) @@ -2670,9 +2504,7 @@ def test_hour(self, locale_list_no_weeks: List[str]): assert arw.dehumanize(hour_future_string, locale=lang) == hour_future def test_hours(self, locale_list_no_weeks: List[str]): - for lang in locale_list_no_weeks: - arw = arrow.Arrow(2010, 2, 16, 7, 55, 0) hour_ago = arw.shift(hours=-3) hour_future = arw.shift(hours=3) @@ -2686,9 +2518,7 @@ def test_hours(self, locale_list_no_weeks: List[str]): assert arw.dehumanize(hour_future_string, locale=lang) == hour_future def test_week(self, locale_list_with_weeks: List[str]): - for lang in locale_list_with_weeks: - arw = arrow.Arrow(2012, 2, 18, 1, 52, 0) week_ago = arw.shift(weeks=-1) week_future = arw.shift(weeks=1) @@ -2702,9 +2532,7 @@ def test_week(self, locale_list_with_weeks: List[str]): assert arw.dehumanize(week_future_string, locale=lang) == week_future def test_weeks(self, locale_list_with_weeks: List[str]): - for lang in locale_list_with_weeks: - arw = arrow.Arrow(2020, 3, 18, 5, 3, 0) week_ago = arw.shift(weeks=-7) week_future = arw.shift(weeks=7) @@ -2718,9 +2546,7 @@ def test_weeks(self, locale_list_with_weeks: List[str]): assert arw.dehumanize(week_future_string, locale=lang) == week_future def test_year(self, locale_list_no_weeks: List[str]): - for lang in locale_list_no_weeks: - arw = arrow.Arrow(2000, 1, 10, 5, 55, 0) year_ago = arw.shift(years=-1) year_future = arw.shift(years=1) @@ -2734,9 +2560,7 @@ def test_year(self, locale_list_no_weeks: List[str]): assert arw.dehumanize(year_future_string, locale=lang) == year_future def test_years(self, locale_list_no_weeks: List[str]): - for lang in locale_list_no_weeks: - arw = arrow.Arrow(2000, 1, 10, 5, 55, 0) year_ago = arw.shift(years=-10) year_future = arw.shift(years=10) @@ -2750,9 +2574,7 @@ def test_years(self, locale_list_no_weeks: List[str]): assert arw.dehumanize(year_future_string, locale=lang) == year_future def test_gt_than_10_years(self, locale_list_no_weeks: List[str]): - for lang in locale_list_no_weeks: - arw = arrow.Arrow(2000, 1, 10, 5, 55, 0) year_ago = arw.shift(years=-25) year_future = arw.shift(years=25) @@ -2766,9 +2588,7 @@ def test_gt_than_10_years(self, locale_list_no_weeks: List[str]): assert arw.dehumanize(year_future_string, locale=lang) == year_future def test_mixed_granularity(self, locale_list_no_weeks: List[str]): - for lang in locale_list_no_weeks: - arw = arrow.Arrow(2000, 1, 10, 5, 55, 0) past = arw.shift(hours=-1, minutes=-1, seconds=-1) future = arw.shift(hours=1, minutes=1, seconds=1) @@ -2784,9 +2604,7 @@ def test_mixed_granularity(self, locale_list_no_weeks: List[str]): assert arw.dehumanize(future_string, locale=lang) == future def test_mixed_granularity_hours(self, locale_list_no_weeks: List[str]): - for lang in locale_list_no_weeks: - arw = arrow.Arrow(2000, 1, 10, 5, 55, 0) past = arw.shift(hours=-3, minutes=-1, seconds=-15) future = arw.shift(hours=3, minutes=1, seconds=15) @@ -2802,9 +2620,7 @@ def test_mixed_granularity_hours(self, locale_list_no_weeks: List[str]): assert arw.dehumanize(future_string, locale=lang) == future def test_mixed_granularity_day(self, locale_list_no_weeks: List[str]): - for lang in locale_list_no_weeks: - arw = arrow.Arrow(2000, 1, 10, 5, 55, 0) past = arw.shift(days=-3, minutes=-1, seconds=-15) future = arw.shift(days=3, minutes=1, seconds=15) @@ -2820,9 +2636,7 @@ def test_mixed_granularity_day(self, locale_list_no_weeks: List[str]): assert arw.dehumanize(future_string, locale=lang) == future def test_mixed_granularity_day_hour(self, locale_list_no_weeks: List[str]): - for lang in locale_list_no_weeks: - arw = arrow.Arrow(2000, 1, 10, 5, 55, 0) past = arw.shift(days=-3, hours=-23, seconds=-15) future = arw.shift(days=3, hours=23, seconds=15) @@ -2839,7 +2653,6 @@ def test_mixed_granularity_day_hour(self, locale_list_no_weeks: List[str]): # Test to make sure unsupported locales error out def test_unsupported_locale(self): - arw = arrow.Arrow(2000, 6, 18, 5, 55, 0) second_ago = arw.shift(seconds=-5) second_future = arw.shift(seconds=5) @@ -2860,7 +2673,6 @@ def test_unsupported_locale(self): # Test to ensure old style locale strings are supported def test_normalized_locale(self): - arw = arrow.Arrow(2000, 6, 18, 5, 55, 0) second_ago = arw.shift(seconds=-5) second_future = arw.shift(seconds=5) @@ -2877,9 +2689,7 @@ def test_normalized_locale(self): # Ensures relative units are required in string def test_require_relative_unit(self, locale_list_no_weeks: List[str]): - for lang in locale_list_no_weeks: - arw = arrow.Arrow(2000, 6, 18, 5, 55, 0) second_ago = arw.shift(seconds=-5) second_future = arw.shift(seconds=5) @@ -2899,9 +2709,7 @@ def test_require_relative_unit(self, locale_list_no_weeks: List[str]): # Test for scrambled input def test_scrambled_input(self, locale_list_no_weeks: List[str]): - for lang in locale_list_no_weeks: - arw = arrow.Arrow(2000, 6, 18, 5, 55, 0) second_ago = arw.shift(seconds=-5) second_future = arw.shift(seconds=5) @@ -2927,9 +2735,7 @@ def test_scrambled_input(self, locale_list_no_weeks: List[str]): arw.dehumanize(second_future_string, locale=lang) def test_no_units_modified(self, locale_list_no_weeks: List[str]): - for lang in locale_list_no_weeks: - arw = arrow.Arrow(2000, 6, 18, 5, 55, 0) # Ensures we pass the first stage of checking whether relative units exist @@ -2944,7 +2750,6 @@ def test_no_units_modified(self, locale_list_no_weeks: List[str]): arw.dehumanize(empty_future_string, locale=lang) def test_slavic_locales(self, slavic_locales: List[str]): - # Relevant units for Slavic locale plural logic units = [ 0, @@ -2975,7 +2780,6 @@ def test_slavic_locales(self, slavic_locales: List[str]): assert arw.dehumanize(future_string, locale=lang) == future def test_czech_slovak(self): - # Relevant units for Slavic locale plural logic units = [ 0, @@ -3082,7 +2886,6 @@ def test_value_error_exception(self): class TestArrowUtil: def test_get_datetime(self): - get_datetime = arrow.Arrow._get_datetime arw = arrow.Arrow.utcnow() @@ -3100,7 +2903,6 @@ def test_get_datetime(self): assert "not recognized as a datetime or timestamp" in str(raise_ctx.value) def test_get_tzinfo(self): - get_tzinfo = arrow.Arrow._get_tzinfo with pytest.raises(ValueError) as raise_ctx: @@ -3108,7 +2910,6 @@ def test_get_tzinfo(self): assert "not recognized as a timezone" in str(raise_ctx.value) def test_get_iteration_params(self): - assert arrow.Arrow._get_iteration_params("end", None) == ("end", sys.maxsize) assert arrow.Arrow._get_iteration_params(None, 100) == (arrow.Arrow.max, 100) assert arrow.Arrow._get_iteration_params(100, 120) == (100, 120) diff --git a/tests/test_factory.py b/tests/test_factory.py index f368126c..4e328000 100644 --- a/tests/test_factory.py +++ b/tests/test_factory.py @@ -14,13 +14,11 @@ @pytest.mark.usefixtures("arrow_factory") class TestGet: def test_no_args(self): - assert_datetime_equality( self.factory.get(), datetime.utcnow().replace(tzinfo=tz.tzutc()) ) def test_timestamp_one_arg_no_arg(self): - no_arg = self.factory.get(1406430900).timestamp() one_arg = self.factory.get("1406430900", "X").timestamp() @@ -31,14 +29,12 @@ def test_one_arg_none(self): self.factory.get(None) def test_struct_time(self): - assert_datetime_equality( self.factory.get(time.gmtime()), datetime.utcnow().replace(tzinfo=tz.tzutc()), ) def test_one_arg_timestamp(self): - int_timestamp = int(time.time()) timestamp_dt = datetime.utcfromtimestamp(int_timestamp).replace( tzinfo=tz.tzutc() @@ -66,7 +62,6 @@ def test_one_arg_timestamp(self): self.factory.get(timestamp) def test_one_arg_expanded_timestamp(self): - millisecond_timestamp = 1591328104308 microsecond_timestamp = 1591328104308505 @@ -79,7 +74,6 @@ def test_one_arg_expanded_timestamp(self): ).replace(tzinfo=tz.tzutc()) def test_one_arg_timestamp_with_tzinfo(self): - timestamp = time.time() timestamp_dt = datetime.fromtimestamp(timestamp, tz=tz.tzutc()).astimezone( tz.gettz("US/Pacific") @@ -91,27 +85,23 @@ def test_one_arg_timestamp_with_tzinfo(self): ) def test_one_arg_arrow(self): - arw = self.factory.utcnow() result = self.factory.get(arw) assert arw == result def test_one_arg_datetime(self): - dt = datetime.utcnow().replace(tzinfo=tz.tzutc()) assert self.factory.get(dt) == dt def test_one_arg_date(self): - d = date.today() dt = datetime(d.year, d.month, d.day, tzinfo=tz.tzutc()) assert self.factory.get(d) == dt def test_one_arg_tzinfo(self): - self.expected = ( datetime.utcnow() .replace(tzinfo=tz.tzutc()) @@ -132,7 +122,6 @@ def test_one_arg_dateparser_datetime(self): assert dt_output == expected def test_kwarg_tzinfo(self): - self.expected = ( datetime.utcnow() .replace(tzinfo=tz.tzutc()) @@ -144,7 +133,6 @@ def test_kwarg_tzinfo(self): ) def test_kwarg_tzinfo_string(self): - self.expected = ( datetime.utcnow() .replace(tzinfo=tz.tzutc()) @@ -176,7 +164,6 @@ def test_kwarg_normalize_whitespace(self): # regression test for #944 def test_one_arg_datetime_tzinfo_kwarg(self): - dt = datetime(2021, 4, 29, 6) result = self.factory.get(dt, tzinfo="America/Chicago") @@ -186,7 +173,6 @@ def test_one_arg_datetime_tzinfo_kwarg(self): assert_datetime_equality(result._datetime, expected) def test_one_arg_arrow_tzinfo_kwarg(self): - arw = Arrow(2021, 4, 29, 6) result = self.factory.get(arw, tzinfo="America/Chicago") @@ -196,7 +182,6 @@ def test_one_arg_arrow_tzinfo_kwarg(self): assert_datetime_equality(result._datetime, expected) def test_one_arg_date_tzinfo_kwarg(self): - da = date(2021, 4, 29) result = self.factory.get(da, tzinfo="America/Chicago") @@ -207,7 +192,6 @@ def test_one_arg_date_tzinfo_kwarg(self): assert result.tzinfo == expected.tzinfo def test_one_arg_iso_calendar_tzinfo_kwarg(self): - result = self.factory.get((2004, 1, 7), tzinfo="America/Chicago") expected = Arrow(2004, 1, 4, tzinfo="America/Chicago") @@ -215,7 +199,6 @@ def test_one_arg_iso_calendar_tzinfo_kwarg(self): assert_datetime_equality(result, expected) def test_one_arg_iso_str(self): - dt = datetime.utcnow() assert_datetime_equality( @@ -223,7 +206,6 @@ def test_one_arg_iso_str(self): ) def test_one_arg_iso_calendar(self): - pairs = [ (datetime(2004, 1, 4), (2004, 1, 7)), (datetime(2008, 12, 30), (2009, 1, 2)), @@ -252,12 +234,10 @@ def test_one_arg_iso_calendar(self): self.factory.get((2014, 7, 10)) def test_one_arg_other(self): - with pytest.raises(TypeError): self.factory.get(object()) def test_one_arg_bool(self): - with pytest.raises(TypeError): self.factory.get(False) @@ -272,47 +252,39 @@ def test_one_arg_decimal(self): ) def test_two_args_datetime_tzinfo(self): - result = self.factory.get(datetime(2013, 1, 1), tz.gettz("US/Pacific")) assert result._datetime == datetime(2013, 1, 1, tzinfo=tz.gettz("US/Pacific")) def test_two_args_datetime_tz_str(self): - result = self.factory.get(datetime(2013, 1, 1), "US/Pacific") assert result._datetime == datetime(2013, 1, 1, tzinfo=tz.gettz("US/Pacific")) def test_two_args_date_tzinfo(self): - result = self.factory.get(date(2013, 1, 1), tz.gettz("US/Pacific")) assert result._datetime == datetime(2013, 1, 1, tzinfo=tz.gettz("US/Pacific")) def test_two_args_date_tz_str(self): - result = self.factory.get(date(2013, 1, 1), "US/Pacific") assert result._datetime == datetime(2013, 1, 1, tzinfo=tz.gettz("US/Pacific")) def test_two_args_datetime_other(self): - with pytest.raises(TypeError): self.factory.get(datetime.utcnow(), object()) def test_two_args_date_other(self): - with pytest.raises(TypeError): self.factory.get(date.today(), object()) def test_two_args_str_str(self): - result = self.factory.get("2013-01-01", "YYYY-MM-DD") assert result._datetime == datetime(2013, 1, 1, tzinfo=tz.tzutc()) def test_two_args_str_tzinfo(self): - result = self.factory.get("2013-01-01", tzinfo=tz.gettz("US/Pacific")) assert_datetime_equality( @@ -320,7 +292,6 @@ def test_two_args_str_tzinfo(self): ) def test_two_args_twitter_format(self): - # format returned by twitter API for created_at: twitter_date = "Fri Apr 08 21:08:54 +0000 2016" result = self.factory.get(twitter_date, "ddd MMM DD HH:mm:ss Z YYYY") @@ -328,24 +299,20 @@ def test_two_args_twitter_format(self): assert result._datetime == datetime(2016, 4, 8, 21, 8, 54, tzinfo=tz.tzutc()) def test_two_args_str_list(self): - result = self.factory.get("2013-01-01", ["MM/DD/YYYY", "YYYY-MM-DD"]) assert result._datetime == datetime(2013, 1, 1, tzinfo=tz.tzutc()) def test_two_args_unicode_unicode(self): - result = self.factory.get("2013-01-01", "YYYY-MM-DD") assert result._datetime == datetime(2013, 1, 1, tzinfo=tz.tzutc()) def test_two_args_other(self): - with pytest.raises(TypeError): self.factory.get(object(), object()) def test_three_args_with_tzinfo(self): - timefmt = "YYYYMMDD" d = "20150514" @@ -354,11 +321,9 @@ def test_three_args_with_tzinfo(self): ) def test_three_args(self): - assert self.factory.get(2013, 1, 1) == datetime(2013, 1, 1, tzinfo=tz.tzutc()) def test_full_kwargs(self): - assert self.factory.get( year=2016, month=7, @@ -370,7 +335,6 @@ def test_full_kwargs(self): ) == datetime(2016, 7, 14, 7, 16, 45, 631092, tzinfo=tz.tzutc()) def test_three_kwargs(self): - assert self.factory.get(year=2016, month=7, day=14) == datetime( 2016, 7, 14, 0, 0, tzinfo=tz.tzutc() ) @@ -380,7 +344,6 @@ def test_tzinfo_string_kwargs(self): assert result._datetime == datetime(2019, 7, 28, 7, 0, 0, 0, tzinfo=tz.tzutc()) def test_insufficient_kwargs(self): - with pytest.raises(TypeError): self.factory.get(year=2016) @@ -409,7 +372,6 @@ def test_locale_with_tzinfo(self): @pytest.mark.usefixtures("arrow_factory") class TestUtcNow: def test_utcnow(self): - assert_datetime_equality( self.factory.utcnow()._datetime, datetime.utcnow().replace(tzinfo=tz.tzutc()), @@ -419,15 +381,12 @@ def test_utcnow(self): @pytest.mark.usefixtures("arrow_factory") class TestNow: def test_no_tz(self): - assert_datetime_equality(self.factory.now(), datetime.now(tz.tzlocal())) def test_tzinfo(self): - assert_datetime_equality( self.factory.now(tz.gettz("EST")), datetime.now(tz.gettz("EST")) ) def test_tz_str(self): - assert_datetime_equality(self.factory.now("EST"), datetime.now(tz.gettz("EST"))) diff --git a/tests/test_formatter.py b/tests/test_formatter.py index 06831f1e..0b6c256c 100644 --- a/tests/test_formatter.py +++ b/tests/test_formatter.py @@ -23,7 +23,6 @@ @pytest.mark.usefixtures("arrow_formatter") class TestFormatterFormatToken: def test_format(self): - dt = datetime(2013, 2, 5, 12, 32, 51) result = self.formatter.format(dt, "MM-DD-YYYY hh:mm:ss a") @@ -31,13 +30,11 @@ def test_format(self): assert result == "02-05-2013 12:32:51 pm" def test_year(self): - dt = datetime(2013, 1, 1) assert self.formatter._format_token(dt, "YYYY") == "2013" assert self.formatter._format_token(dt, "YY") == "13" def test_month(self): - dt = datetime(2013, 1, 1) assert self.formatter._format_token(dt, "MMMM") == "January" assert self.formatter._format_token(dt, "MMM") == "Jan" @@ -45,7 +42,6 @@ def test_month(self): assert self.formatter._format_token(dt, "M") == "1" def test_day(self): - dt = datetime(2013, 2, 1) assert self.formatter._format_token(dt, "DDDD") == "032" assert self.formatter._format_token(dt, "DDD") == "32" @@ -58,7 +54,6 @@ def test_day(self): assert self.formatter._format_token(dt, "d") == "5" def test_hour(self): - dt = datetime(2013, 1, 1, 2) assert self.formatter._format_token(dt, "HH") == "02" assert self.formatter._format_token(dt, "H") == "2" @@ -81,19 +76,16 @@ def test_hour(self): assert self.formatter._format_token(dt, "h") == "12" def test_minute(self): - dt = datetime(2013, 1, 1, 0, 1) assert self.formatter._format_token(dt, "mm") == "01" assert self.formatter._format_token(dt, "m") == "1" def test_second(self): - dt = datetime(2013, 1, 1, 0, 0, 1) assert self.formatter._format_token(dt, "ss") == "01" assert self.formatter._format_token(dt, "s") == "1" def test_sub_second(self): - dt = datetime(2013, 1, 1, 0, 0, 0, 123456) assert self.formatter._format_token(dt, "SSSSSS") == "123456" assert self.formatter._format_token(dt, "SSSSS") == "12345" @@ -111,7 +103,6 @@ def test_sub_second(self): assert self.formatter._format_token(dt, "S") == "0" def test_timestamp(self): - dt = datetime.now(tz=dateutil_tz.UTC) expected = str(dt.timestamp()) assert self.formatter._format_token(dt, "X") == expected @@ -122,7 +113,6 @@ def test_timestamp(self): assert self.formatter._format_token(dt, "x") == expected def test_timezone(self): - dt = datetime.utcnow().replace(tzinfo=dateutil_tz.gettz("US/Pacific")) result = self.formatter._format_token(dt, "ZZ") @@ -133,7 +123,6 @@ def test_timezone(self): @pytest.mark.parametrize("full_tz_name", make_full_tz_list()) def test_timezone_formatter(self, full_tz_name): - # This test will fail if we use "now" as date as soon as we change from/to DST dt = datetime(1986, 2, 14, tzinfo=pytz.timezone("UTC")).replace( tzinfo=dateutil_tz.gettz(full_tz_name) @@ -144,7 +133,6 @@ def test_timezone_formatter(self, full_tz_name): assert result == abbreviation def test_am_pm(self): - dt = datetime(2012, 1, 1, 11) assert self.formatter._format_token(dt, "a") == "am" assert self.formatter._format_token(dt, "A") == "AM" @@ -167,7 +155,6 @@ def test_nonsense(self): assert self.formatter._format_token(dt, "NONSENSE") is None def test_escape(self): - assert ( self.formatter.format( datetime(2015, 12, 10, 17, 9), "MMMM D, YYYY [at] h:mma" diff --git a/tests/test_locales.py b/tests/test_locales.py index 4bbbd3dc..d6abb2a1 100644 --- a/tests/test_locales.py +++ b/tests/test_locales.py @@ -8,7 +8,6 @@ class TestLocaleValidation: """Validate locales to ensure that translations are valid and complete""" def test_locale_validation(self): - for locale_cls in self.locales.values(): # 7 days + 1 spacer to allow for 1-indexing of months assert len(locale_cls.day_names) == 8 @@ -34,7 +33,6 @@ def test_locale_validation(self): assert locale_cls.future is not None def test_locale_name_validation(self): - for locale_cls in self.locales.values(): for locale_name in locale_cls.names: assert len(locale_name) == 2 or len(locale_name) == 5 @@ -90,7 +88,6 @@ def test_get_locale_by_class_name(self, mocker): assert result == mock_locale_obj def test_locales(self): - assert len(locales._locale_map) > 0 @@ -116,24 +113,20 @@ def test_describe(self): assert self.locale.describe("now", only_distance=False) == "just now" def test_format_timeframe(self): - assert self.locale._format_timeframe("hours", 2) == "2 hours" assert self.locale._format_timeframe("hour", 0) == "an hour" def test_format_relative_now(self): - result = self.locale._format_relative("just now", "now", 0) assert result == "just now" def test_format_relative_past(self): - result = self.locale._format_relative("an hour", "hour", 1) assert result == "in an hour" def test_format_relative_future(self): - result = self.locale._format_relative("an hour", "hour", -1) assert result == "an hour ago" @@ -438,7 +431,6 @@ def test_plurals2(self): @pytest.mark.usefixtures("lang_locale") class TestPolishLocale: def test_plurals(self): - assert self.locale._format_timeframe("seconds", 0) == "0 sekund" assert self.locale._format_timeframe("second", 1) == "sekundę" assert self.locale._format_timeframe("seconds", 2) == "2 sekundy" @@ -491,7 +483,6 @@ def test_plurals(self): @pytest.mark.usefixtures("lang_locale") class TestIcelandicLocale: def test_format_timeframe(self): - assert self.locale._format_timeframe("now", 0) == "rétt í þessu" assert self.locale._format_timeframe("second", -1) == "sekúndu" @@ -534,23 +525,19 @@ def test_format_timeframe(self): @pytest.mark.usefixtures("lang_locale") class TestMalayalamLocale: def test_format_timeframe(self): - assert self.locale._format_timeframe("hours", 2) == "2 മണിക്കൂർ" assert self.locale._format_timeframe("hour", 0) == "ഒരു മണിക്കൂർ" def test_format_relative_now(self): - result = self.locale._format_relative("ഇപ്പോൾ", "now", 0) assert result == "ഇപ്പോൾ" def test_format_relative_past(self): - result = self.locale._format_relative("ഒരു മണിക്കൂർ", "hour", 1) assert result == "ഒരു മണിക്കൂർ ശേഷം" def test_format_relative_future(self): - result = self.locale._format_relative("ഒരു മണിക്കൂർ", "hour", -1) assert result == "ഒരു മണിക്കൂർ മുമ്പ്" @@ -585,22 +572,18 @@ def test_weekday(self): @pytest.mark.usefixtures("lang_locale") class TestHindiLocale: def test_format_timeframe(self): - assert self.locale._format_timeframe("hours", 2) == "2 घंटे" assert self.locale._format_timeframe("hour", 0) == "एक घंटा" def test_format_relative_now(self): - result = self.locale._format_relative("अभी", "now", 0) assert result == "अभी" def test_format_relative_past(self): - result = self.locale._format_relative("एक घंटा", "hour", 1) assert result == "एक घंटा बाद" def test_format_relative_future(self): - result = self.locale._format_relative("एक घंटा", "hour", -1) assert result == "एक घंटा पहले" @@ -675,17 +658,14 @@ def test_format_timeframe(self): assert self.locale._format_timeframe("years", 5) == "5 let" def test_format_relative_now(self): - result = self.locale._format_relative("Teď", "now", 0) assert result == "Teď" def test_format_relative_future(self): - result = self.locale._format_relative("hodinu", "hour", 1) assert result == "Za hodinu" def test_format_relative_past(self): - result = self.locale._format_relative("hodinou", "hour", -1) assert result == "Před hodinou" @@ -693,7 +673,6 @@ def test_format_relative_past(self): @pytest.mark.usefixtures("lang_locale") class TestSlovakLocale: def test_format_timeframe(self): - assert self.locale._format_timeframe("seconds", -5) == "5 sekundami" assert self.locale._format_timeframe("seconds", -2) == "2 sekundami" assert self.locale._format_timeframe("second", -1) == "sekundou" @@ -753,17 +732,14 @@ def test_format_timeframe(self): assert self.locale._format_timeframe("now", 0) == "Teraz" def test_format_relative_now(self): - result = self.locale._format_relative("Teraz", "now", 0) assert result == "Teraz" def test_format_relative_future(self): - result = self.locale._format_relative("hodinu", "hour", 1) assert result == "O hodinu" def test_format_relative_past(self): - result = self.locale._format_relative("hodinou", "hour", -1) assert result == "Pred hodinou" @@ -1400,7 +1376,6 @@ def test_ordinal_number(self): @pytest.mark.usefixtures("lang_locale") class TestRomanianLocale: def test_timeframes(self): - assert self.locale._format_timeframe("hours", 2) == "2 ore" assert self.locale._format_timeframe("months", 2) == "2 luni" @@ -1435,7 +1410,6 @@ def test_relative_timeframes(self): @pytest.mark.usefixtures("lang_locale") class TestArabicLocale: def test_timeframes(self): - # single assert self.locale._format_timeframe("minute", 1) == "دقيقة" assert self.locale._format_timeframe("hour", 1) == "ساعة" @@ -2514,22 +2488,18 @@ def test_ordinal_number(self): assert self.locale._ordinal_number(-1) == "" def test_format_timeframe(self): - assert self.locale._format_timeframe("hours", 2) == "2 ଘଣ୍ଟା" assert self.locale._format_timeframe("hour", 0) == "ଏକ ଘଣ୍ଟା" def test_format_relative_now(self): - result = self.locale._format_relative("ବର୍ତ୍ତମାନ", "now", 0) assert result == "ବର୍ତ୍ତମାନ" def test_format_relative_past(self): - result = self.locale._format_relative("ଏକ ଘଣ୍ଟା", "hour", 1) assert result == "ଏକ ଘଣ୍ଟା ପରେ" def test_format_relative_future(self): - result = self.locale._format_relative("ଏକ ଘଣ୍ଟା", "hour", -1) assert result == "ଏକ ଘଣ୍ଟା ପୂର୍ବେ" @@ -2758,13 +2728,11 @@ def test_format_relative_now(self): assert result == "දැන්" def test_format_relative_future(self): - result = self.locale._format_relative("පැයකින්", "පැය", 1) assert result == "පැයකින්" # (in) one hour def test_format_relative_past(self): - result = self.locale._format_relative("පැයක", "පැය", -1) assert result == "පැයකට පෙර" # an hour ago @@ -2868,24 +2836,20 @@ def test_ordinal_number(self): assert self.locale.ordinal_number(1) == "1." def test_format_timeframe(self): - assert self.locale._format_timeframe("hours", 2) == "2 timer" assert self.locale._format_timeframe("hour", 0) == "en time" def test_format_relative_now(self): - result = self.locale._format_relative("nå nettopp", "now", 0) assert result == "nå nettopp" def test_format_relative_past(self): - result = self.locale._format_relative("en time", "hour", 1) assert result == "om en time" def test_format_relative_future(self): - result = self.locale._format_relative("en time", "hour", -1) assert result == "for en time siden" @@ -2924,24 +2888,20 @@ def test_ordinal_number(self): assert self.locale.ordinal_number(1) == "1." def test_format_timeframe(self): - assert self.locale._format_timeframe("hours", 2) == "2 timar" assert self.locale._format_timeframe("hour", 0) == "ein time" def test_format_relative_now(self): - result = self.locale._format_relative("no nettopp", "now", 0) assert result == "no nettopp" def test_format_relative_past(self): - result = self.locale._format_relative("ein time", "hour", 1) assert result == "om ein time" def test_format_relative_future(self): - result = self.locale._format_relative("ein time", "hour", -1) assert result == "for ein time sidan" @@ -3063,13 +3023,11 @@ def test_ordinal_number(self): assert self.locale.ordinal_number(1) == "1ኛ" def test_format_relative_future(self): - result = self.locale._format_relative("በአንድ ሰዓት", "hour", 1) assert result == "በአንድ ሰዓት ውስጥ" # (in) one hour def test_format_relative_past(self): - result = self.locale._format_relative("ከአንድ ሰዓት", "hour", -1) assert result == "ከአንድ ሰዓት በፊት" # an hour ago diff --git a/tests/test_parser.py b/tests/test_parser.py index bdcc1026..1932b450 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -83,7 +83,6 @@ def test_parse_token_invalid_meridians(self): assert parts == {} def test_parser_no_caching(self, mocker): - mocked_parser = mocker.patch( "arrow.parser.DateTimeParser._generate_pattern_re", fmt="fmt_a" ) @@ -135,7 +134,6 @@ def test_parser_multiple_line_caching(self, mocker): assert mocked_parser.call_args_list[1] == mocker.call(fmt="fmt_b") def test_YY_and_YYYY_format_list(self): - assert self.parser.parse("15/01/19", ["DD/MM/YY", "DD/MM/YYYY"]) == datetime( 2019, 1, 15 ) @@ -165,7 +163,6 @@ def test_timestamp_format_list(self): @pytest.mark.usefixtures("dt_parser") class TestDateTimeParserParse: def test_parse_list(self, mocker): - mocker.patch( "arrow.parser.DateTimeParser._parse_multiformat", string="str", @@ -177,7 +174,6 @@ def test_parse_list(self, mocker): assert result == "result" def test_parse_unrecognized_token(self, mocker): - mocker.patch.dict("arrow.parser.DateTimeParser._BASE_INPUT_RE_MAP") del arrow.parser.DateTimeParser._BASE_INPUT_RE_MAP["YYYY"] @@ -187,17 +183,14 @@ def test_parse_unrecognized_token(self, mocker): _parser.parse("2013-01-01", "YYYY-MM-DD") def test_parse_parse_no_match(self): - with pytest.raises(ParserError): self.parser.parse("01-01", "YYYY-MM-DD") def test_parse_separators(self): - with pytest.raises(ParserError): self.parser.parse("1403549231", "YYYY-MM-DD") def test_parse_numbers(self): - self.expected = datetime(2012, 1, 1, 12, 5, 10) assert ( self.parser.parse("2012-01-01 12:05:10", "YYYY-MM-DD HH:mm:ss") @@ -205,19 +198,16 @@ def test_parse_numbers(self): ) def test_parse_am(self): - with pytest.raises(ParserMatchError): self.parser.parse("2021-01-30 14:00:00 AM", "YYYY-MM-DD HH:mm:ss A") def test_parse_year_two_digit(self): - self.expected = datetime(1979, 1, 1, 12, 5, 10) assert ( self.parser.parse("79-01-01 12:05:10", "YY-MM-DD HH:mm:ss") == self.expected ) def test_parse_timestamp(self): - tz_utc = tz.tzutc() float_timestamp = time.time() int_timestamp = int(float_timestamp) @@ -296,14 +286,12 @@ def test_parse_expanded_timestamp(self): self.parser.parse(f"{timestamp:f}", "x") def test_parse_names(self): - self.expected = datetime(2012, 1, 1) assert self.parser.parse("January 1, 2012", "MMMM D, YYYY") == self.expected assert self.parser.parse("Jan 1, 2012", "MMM D, YYYY") == self.expected def test_parse_pm(self): - self.expected = datetime(1, 1, 1, 13, 0, 0) assert self.parser.parse("1 pm", "H a") == self.expected assert self.parser.parse("1 pm", "h a") == self.expected @@ -321,19 +309,16 @@ def test_parse_pm(self): assert self.parser.parse("12 pm", "h A") == self.expected def test_parse_tz_hours_only(self): - self.expected = datetime(2025, 10, 17, 5, 30, 10, tzinfo=tz.tzoffset(None, 0)) parsed = self.parser.parse("2025-10-17 05:30:10+00", "YYYY-MM-DD HH:mm:ssZ") assert parsed == self.expected def test_parse_tz_zz(self): - self.expected = datetime(2013, 1, 1, tzinfo=tz.tzoffset(None, -7 * 3600)) assert self.parser.parse("2013-01-01 -07:00", "YYYY-MM-DD ZZ") == self.expected @pytest.mark.parametrize("full_tz_name", make_full_tz_list()) def test_parse_tz_name_zzz(self, full_tz_name): - self.expected = datetime(2013, 1, 1, tzinfo=tz.gettz(full_tz_name)) assert ( self.parser.parse(f"2013-01-01 {full_tz_name}", "YYYY-MM-DD ZZZ") @@ -727,7 +712,6 @@ def test_parse_HH_24(self): self.parser.parse("2019-12-31T24:00:00.999999", "YYYY-MM-DDTHH:mm:ss.S") def test_parse_W(self): - assert self.parser.parse("2011-W05-4", "W") == datetime(2011, 2, 3) assert self.parser.parse("2011W054", "W") == datetime(2011, 2, 3) assert self.parser.parse("2011-W05", "W") == datetime(2011, 1, 31) @@ -794,31 +778,24 @@ def test_parse_normalize_whitespace(self): @pytest.mark.usefixtures("dt_parser_regex") class TestDateTimeParserRegex: def test_format_year(self): - assert self.format_regex.findall("YYYY-YY") == ["YYYY", "YY"] def test_format_month(self): - assert self.format_regex.findall("MMMM-MMM-MM-M") == ["MMMM", "MMM", "MM", "M"] def test_format_day(self): - assert self.format_regex.findall("DDDD-DDD-DD-D") == ["DDDD", "DDD", "DD", "D"] def test_format_hour(self): - assert self.format_regex.findall("HH-H-hh-h") == ["HH", "H", "hh", "h"] def test_format_minute(self): - assert self.format_regex.findall("mm-m") == ["mm", "m"] def test_format_second(self): - assert self.format_regex.findall("ss-s") == ["ss", "s"] def test_format_subsecond(self): - assert self.format_regex.findall("SSSSSS-SSSSS-SSSS-SSS-SS-S") == [ "SSSSSS", "SSSSS", @@ -829,23 +806,18 @@ def test_format_subsecond(self): ] def test_format_tz(self): - assert self.format_regex.findall("ZZZ-ZZ-Z") == ["ZZZ", "ZZ", "Z"] def test_format_am_pm(self): - assert self.format_regex.findall("A-a") == ["A", "a"] def test_format_timestamp(self): - assert self.format_regex.findall("X") == ["X"] def test_format_timestamp_milli(self): - assert self.format_regex.findall("x") == ["x"] def test_escape(self): - escape_regex = parser.DateTimeParser._ESCAPE_RE assert escape_regex.findall("2018-03-09 8 [h] 40 [hello]") == ["[h]", "[hello]"] @@ -869,7 +841,6 @@ def test_month_abbreviations(self): assert result == calendar.month_abbr[1:] def test_digits(self): - assert parser.DateTimeParser._ONE_OR_TWO_DIGIT_RE.findall("4-56") == ["4", "56"] assert parser.DateTimeParser._ONE_OR_TWO_OR_THREE_DIGIT_RE.findall( "4-56-789" @@ -940,7 +911,6 @@ def test_time(self): @pytest.mark.usefixtures("dt_parser") class TestDateTimeParserISO: def test_YYYY(self): - assert self.parser.parse_iso("2013") == datetime(2013, 1, 1) def test_YYYY_DDDD(self): @@ -968,7 +938,6 @@ def test_YYYY_DDDD(self): assert self.parser.parse_iso("2017-366") == datetime(2018, 1, 1) def test_YYYY_DDDD_HH_mm_ssZ(self): - assert self.parser.parse_iso("2013-036 04:05:06+01:00") == datetime( 2013, 2, 5, 4, 5, 6, tzinfo=tz.tzoffset(None, 3600) ) @@ -982,67 +951,55 @@ def test_YYYY_MM_DDDD(self): self.parser.parse_iso("2014-05-125") def test_YYYY_MM(self): - for separator in DateTimeParser.SEPARATORS: assert self.parser.parse_iso(separator.join(("2013", "02"))) == datetime( 2013, 2, 1 ) def test_YYYY_MM_DD(self): - for separator in DateTimeParser.SEPARATORS: assert self.parser.parse_iso( separator.join(("2013", "02", "03")) ) == datetime(2013, 2, 3) def test_YYYY_MM_DDTHH_mmZ(self): - assert self.parser.parse_iso("2013-02-03T04:05+01:00") == datetime( 2013, 2, 3, 4, 5, tzinfo=tz.tzoffset(None, 3600) ) def test_YYYY_MM_DDTHH_mm(self): - assert self.parser.parse_iso("2013-02-03T04:05") == datetime(2013, 2, 3, 4, 5) def test_YYYY_MM_DDTHH(self): - assert self.parser.parse_iso("2013-02-03T04") == datetime(2013, 2, 3, 4) def test_YYYY_MM_DDTHHZ(self): - assert self.parser.parse_iso("2013-02-03T04+01:00") == datetime( 2013, 2, 3, 4, tzinfo=tz.tzoffset(None, 3600) ) def test_YYYY_MM_DDTHH_mm_ssZ(self): - assert self.parser.parse_iso("2013-02-03T04:05:06+01:00") == datetime( 2013, 2, 3, 4, 5, 6, tzinfo=tz.tzoffset(None, 3600) ) def test_YYYY_MM_DDTHH_mm_ss(self): - assert self.parser.parse_iso("2013-02-03T04:05:06") == datetime( 2013, 2, 3, 4, 5, 6 ) def test_YYYY_MM_DD_HH_mmZ(self): - assert self.parser.parse_iso("2013-02-03 04:05+01:00") == datetime( 2013, 2, 3, 4, 5, tzinfo=tz.tzoffset(None, 3600) ) def test_YYYY_MM_DD_HH_mm(self): - assert self.parser.parse_iso("2013-02-03 04:05") == datetime(2013, 2, 3, 4, 5) def test_YYYY_MM_DD_HH(self): - assert self.parser.parse_iso("2013-02-03 04") == datetime(2013, 2, 3, 4) def test_invalid_time(self): - with pytest.raises(ParserError): self.parser.parse_iso("2013-02-03T") @@ -1053,19 +1010,16 @@ def test_invalid_time(self): self.parser.parse_iso("2013-02-03 04:05:06.") def test_YYYY_MM_DD_HH_mm_ssZ(self): - assert self.parser.parse_iso("2013-02-03 04:05:06+01:00") == datetime( 2013, 2, 3, 4, 5, 6, tzinfo=tz.tzoffset(None, 3600) ) def test_YYYY_MM_DD_HH_mm_ss(self): - assert self.parser.parse_iso("2013-02-03 04:05:06") == datetime( 2013, 2, 3, 4, 5, 6 ) def test_YYYY_MM_DDTHH_mm_ss_S(self): - assert self.parser.parse_iso("2013-02-03T04:05:06.7") == datetime( 2013, 2, 3, 4, 5, 6, 700000 ) @@ -1100,7 +1054,6 @@ def test_YYYY_MM_DDTHH_mm_ss_S(self): ) def test_YYYY_MM_DDTHH_mm_ss_SZ(self): - assert self.parser.parse_iso("2013-02-03T04:05:06.7+01:00") == datetime( 2013, 2, 3, 4, 5, 6, 700000, tzinfo=tz.tzoffset(None, 3600) ) @@ -1126,7 +1079,6 @@ def test_YYYY_MM_DDTHH_mm_ss_SZ(self): ) def test_W(self): - assert self.parser.parse_iso("2011-W05-4") == datetime(2011, 2, 3) assert self.parser.parse_iso("2011-W05-4T14:17:01") == datetime( @@ -1140,7 +1092,6 @@ def test_W(self): ) def test_invalid_Z(self): - with pytest.raises(ParserError): self.parser.parse_iso("2013-02-03T04:05:06.78912z") @@ -1198,7 +1149,6 @@ def test_gnu_date(self): ) def test_isoformat(self): - dt = datetime.utcnow() assert self.parser.parse_iso(dt.isoformat()) == dt @@ -1359,11 +1309,9 @@ def test_midnight_end_day(self): @pytest.mark.usefixtures("tzinfo_parser") class TestTzinfoParser: def test_parse_local(self): - assert self.parser.parse("local") == tz.tzlocal() def test_parse_utc(self): - assert self.parser.parse("utc") == tz.tzutc() assert self.parser.parse("UTC") == tz.tzutc() @@ -1376,7 +1324,6 @@ def test_parse_utc_withoffset(self): ) == tz.tzoffset(None, 3600) def test_parse_iso(self): - assert self.parser.parse("01:00") == tz.tzoffset(None, 3600) assert self.parser.parse("11:35") == tz.tzoffset(None, 11 * 3600 + 2100) assert self.parser.parse("+01:00") == tz.tzoffset(None, 3600) @@ -1391,11 +1338,9 @@ def test_parse_iso(self): assert self.parser.parse("-01") == tz.tzoffset(None, -3600) def test_parse_str(self): - assert self.parser.parse("US/Pacific") == tz.gettz("US/Pacific") def test_parse_fails(self): - with pytest.raises(parser.ParserError): self.parser.parse("fail") @@ -1403,31 +1348,25 @@ def test_parse_fails(self): @pytest.mark.usefixtures("dt_parser") class TestDateTimeParserMonthName: def test_shortmonth_capitalized(self): - assert self.parser.parse("2013-Jan-01", "YYYY-MMM-DD") == datetime(2013, 1, 1) def test_shortmonth_allupper(self): - assert self.parser.parse("2013-JAN-01", "YYYY-MMM-DD") == datetime(2013, 1, 1) def test_shortmonth_alllower(self): - assert self.parser.parse("2013-jan-01", "YYYY-MMM-DD") == datetime(2013, 1, 1) def test_month_capitalized(self): - assert self.parser.parse("2013-January-01", "YYYY-MMMM-DD") == datetime( 2013, 1, 1 ) def test_month_allupper(self): - assert self.parser.parse("2013-JANUARY-01", "YYYY-MMMM-DD") == datetime( 2013, 1, 1 ) def test_month_alllower(self): - assert self.parser.parse("2013-january-01", "YYYY-MMMM-DD") == datetime( 2013, 1, 1 ) @@ -1574,13 +1513,11 @@ def test_french(self): @pytest.mark.usefixtures("dt_parser") class TestDateTimeParserSearchDate: def test_parse_search(self): - assert self.parser.parse( "Today is 25 of September of 2003", "DD of MMMM of YYYY" ) == datetime(2003, 9, 25) def test_parse_search_with_numbers(self): - assert self.parser.parse( "2000 people met the 2012-01-01 12:05:10", "YYYY-MM-DD HH:mm:ss" ) == datetime(2012, 1, 1, 12, 5, 10) @@ -1590,7 +1527,6 @@ def test_parse_search_with_numbers(self): ) == datetime(1979, 1, 1, 12, 5, 10) def test_parse_search_with_names(self): - assert self.parser.parse("June was born in May 1980", "MMMM YYYY") == datetime( 1980, 5, 1 ) @@ -1607,12 +1543,10 @@ def test_parse_search_locale_with_names(self): ) def test_parse_search_fails(self): - with pytest.raises(parser.ParserError): self.parser.parse("Jag föddes den 25 Augusti 1975", "DD MMMM YYYY") def test_escape(self): - format = "MMMM D, YYYY [at] h:mma" assert self.parser.parse( "Thursday, December 10, 2015 at 5:09pm", format diff --git a/tox.ini b/tox.ini index 11d70cb2..03c4d2af 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,6 @@ [tox] minversion = 3.18.0 -envlist = py{py3,36,37,38,39,310,311} +envlist = py{py3,38,39,310,311,312} skip_missing_interpreters = true [gh-actions] @@ -12,6 +12,7 @@ python = 3.9: py39 3.10: py310 3.11: py311 + 3.12: py312 [testenv] deps = -r requirements/requirements-tests.txt From 1b957339101e9bd8cd7abe1aa5ddf8ede98d56cf Mon Sep 17 00:00:00 2001 From: ville <118881666+vreima@users.noreply.github.com> Date: Sat, 30 Sep 2023 23:55:54 +0300 Subject: [PATCH 40/44] Added translation for weeks in Finnish locale (#1157) Co-authored-by: Jad Chaar --- arrow/locales.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arrow/locales.py b/arrow/locales.py index b2e7ee03..fe8d4395 100644 --- a/arrow/locales.py +++ b/arrow/locales.py @@ -873,6 +873,8 @@ class FinnishLocale(Locale): "hours": {"past": "{0} tuntia", "future": "{0} tunnin"}, "day": {"past": "päivä", "future": "päivän"}, "days": {"past": "{0} päivää", "future": "{0} päivän"}, + "week": {"past": "viikko", "future": "viikon"}, + "weeks": {"past": "{0} viikkoa", "future": "{0} viikon"}, "month": {"past": "kuukausi", "future": "kuukauden"}, "months": {"past": "{0} kuukautta", "future": "{0} kuukauden"}, "year": {"past": "vuosi", "future": "vuoden"}, From 3a6cd95389dfe1c93684aa083a6e3db408269fdb Mon Sep 17 00:00:00 2001 From: Jad Chaar Date: Sat, 30 Sep 2023 14:04:59 -0700 Subject: [PATCH 41/44] Add python dateutil types --- .pre-commit-config.yaml | 2 +- requirements/requirements.txt | 1 + setup.cfg | 2 +- setup.py | 5 ++++- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b8a60a2c..b99f4c48 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -22,7 +22,7 @@ repos: hooks: - id: isort - repo: https://github.com/asottile/pyupgrade - rev: v3.10.1 + rev: v3.13.0 hooks: - id: pyupgrade args: [--py36-plus] diff --git a/requirements/requirements.txt b/requirements/requirements.txt index 65134a19..7d97ff8e 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -1 +1,2 @@ python-dateutil>=2.7.0 +types-python-dateutil>=2.8.10 diff --git a/setup.cfg b/setup.cfg index 6ffbd02b..916477e5 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [mypy] -python_version = 3.6 +python_version = 3.11 show_error_codes = True pretty = True diff --git a/setup.py b/setup.py index a2d8921e..d58b9f3b 100644 --- a/setup.py +++ b/setup.py @@ -22,7 +22,10 @@ package_data={"arrow": ["py.typed"]}, zip_safe=False, python_requires=">=3.8", - install_requires=["python-dateutil>=2.7.0"], + install_requires=[ + "python-dateutil>=2.7.0", + "types-python-dateutil>=2.8.10", + ], classifiers=[ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", From 522a65b16742a76b21154efaff3c3768e95a9bcc Mon Sep 17 00:00:00 2001 From: Abdullah Alajmi <16920216+AbdullahAlajmi@users.noreply.github.com> Date: Sun, 1 Oct 2023 00:07:00 +0300 Subject: [PATCH 42/44] add 'week' and 'weeks' to Arabic locale (#1155) Co-authored-by: Jad Chaar --- arrow/locales.py | 2 ++ tests/test_locales.py | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/arrow/locales.py b/arrow/locales.py index fe8d4395..34b2a098 100644 --- a/arrow/locales.py +++ b/arrow/locales.py @@ -2549,6 +2549,8 @@ class ArabicLocale(Locale): "hours": {"2": "ساعتين", "ten": "{0} ساعات", "higher": "{0} ساعة"}, "day": "يوم", "days": {"2": "يومين", "ten": "{0} أيام", "higher": "{0} يوم"}, + "week": "اسبوع", + "weeks": {"2": "اسبوعين", "ten": "{0} أسابيع", "higher": "{0} اسبوع"}, "month": "شهر", "months": {"2": "شهرين", "ten": "{0} أشهر", "higher": "{0} شهر"}, "year": "سنة", diff --git a/tests/test_locales.py b/tests/test_locales.py index d6abb2a1..cd14274e 100644 --- a/tests/test_locales.py +++ b/tests/test_locales.py @@ -1414,6 +1414,7 @@ def test_timeframes(self): assert self.locale._format_timeframe("minute", 1) == "دقيقة" assert self.locale._format_timeframe("hour", 1) == "ساعة" assert self.locale._format_timeframe("day", 1) == "يوم" + assert self.locale._format_timeframe("week", 1) == "اسبوع" assert self.locale._format_timeframe("month", 1) == "شهر" assert self.locale._format_timeframe("year", 1) == "سنة" @@ -1421,6 +1422,7 @@ def test_timeframes(self): assert self.locale._format_timeframe("minutes", 2) == "دقيقتين" assert self.locale._format_timeframe("hours", 2) == "ساعتين" assert self.locale._format_timeframe("days", 2) == "يومين" + assert self.locale._format_timeframe("weeks", 2) == "اسبوعين" assert self.locale._format_timeframe("months", 2) == "شهرين" assert self.locale._format_timeframe("years", 2) == "سنتين" @@ -1428,12 +1430,14 @@ def test_timeframes(self): assert self.locale._format_timeframe("minutes", 3) == "3 دقائق" assert self.locale._format_timeframe("hours", 4) == "4 ساعات" assert self.locale._format_timeframe("days", 5) == "5 أيام" + assert self.locale._format_timeframe("weeks", 7) == "7 أسابيع" assert self.locale._format_timeframe("months", 6) == "6 أشهر" assert self.locale._format_timeframe("years", 10) == "10 سنوات" # more than ten assert self.locale._format_timeframe("minutes", 11) == "11 دقيقة" assert self.locale._format_timeframe("hours", 19) == "19 ساعة" + assert self.locale._format_timeframe("weeks", 20) == "20 اسبوع" assert self.locale._format_timeframe("months", 24) == "24 شهر" assert self.locale._format_timeframe("days", 50) == "50 يوم" assert self.locale._format_timeframe("years", 115) == "115 سنة" From 431e9793be2d353f11b48532f9e96d5486f778f0 Mon Sep 17 00:00:00 2001 From: Jad Chaar Date: Sat, 30 Sep 2023 14:49:15 -0700 Subject: [PATCH 43/44] Migrate to pyproject.toml (#1164) * Migrate to pyproject.toml * Downgrade pytz --- .github/workflows/release.yml | 30 +++++++++++++ MANIFEST.in | 4 -- Makefile | 16 +++---- pyproject.toml | 66 +++++++++++++++++++++++++++++ requirements/requirements-tests.txt | 2 - setup.py | 48 --------------------- tox.ini | 9 ++++ 7 files changed, 110 insertions(+), 65 deletions(-) create mode 100644 .github/workflows/release.yml delete mode 100644 MANIFEST.in create mode 100644 pyproject.toml delete mode 100644 setup.py diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..35e5fb73 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,30 @@ +name: release + +on: + workflow_dispatch: # run manually + push: # run on matching tags + tags: + - '*.*.*' + +jobs: + release-to-pypi: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/cache@v3 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements*.txt') }} + restore-keys: ${{ runner.os }}-pip- + - uses: actions/setup-python@v4 + with: + python-version: "3.11" + - name: Install dependencies + run: | + pip install -U pip setuptools wheel + pip install -U tox + - name: Publish package to PyPI + env: + FLIT_USERNAME: __token__ + FLIT_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} + run: tox -e publish diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index 9abe9773..00000000 --- a/MANIFEST.in +++ /dev/null @@ -1,4 +0,0 @@ -include LICENSE CHANGELOG.rst README.rst Makefile tox.ini -recursive-include requirements *.txt -recursive-include tests *.py -recursive-include docs *.py *.rst *.bat Makefile diff --git a/Makefile b/Makefile index 27d5cbe4..9db6fba0 100644 --- a/Makefile +++ b/Makefile @@ -23,7 +23,7 @@ test: lint: . venv/bin/activate; \ - pre-commit run --all-files + pre-commit run --all-files --show-diff-on-failure clean-docs: rm -rf docs/_build @@ -42,15 +42,9 @@ clean: clean-dist rm -f .coverage coverage.xml ./**/*.pyc clean-dist: - rm -rf dist build .egg .eggs arrow.egg-info + rm -rf dist build *.egg *.eggs *.egg-info -build-dist: +build-dist: clean-dist . venv/bin/activate; \ - pip install -U pip setuptools twine wheel; \ - python setup.py sdist bdist_wheel - -upload-dist: - . venv/bin/activate; \ - twine upload dist/* - -publish: test clean-dist build-dist upload-dist clean-dist + pip install -U flit; \ + flit build diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..5ccc497a --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,66 @@ +[build-system] +requires = ["flit_core >=3.2,<4"] +build-backend = "flit_core.buildapi" + +[project] +name = "arrow" +authors = [{name = "Chris Smith", email = "crsmithdev@gmail.com"}] +readme = "README.rst" +license = {file = "LICENSE"} +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "Intended Audience :: Information Technology", + "License :: OSI Approved :: Apache Software License", + "Topic :: Software Development :: Libraries :: Python Modules", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Operating System :: OS Independent", +] +dependencies = [ + "python-dateutil>=2.7.0", + "types-python-dateutil>=2.8.10", +] +requires-python = ">=3.8" +description = "Better dates & times for Python" +keywords = [ + "arrow", + "date", + "time", + "datetime", + "timestamp", + "timezone", + "humanize", +] +dynamic = ["version"] + +[project.optional-dependencies] +test = [ + "dateparser==1.*", + "pre-commit", + "pytest", + "pytest-cov", + "pytest-mock", + "pytz==2021.1", + "simplejson==3.*", +] +doc = [ + "doc8", + "sphinx>=7.0.0", + "sphinx-autobuild", + "sphinx-autodoc-typehints", + "sphinx_rtd_theme>=1.3.0", +] + +[project.urls] +Documentation = "https://arrow.readthedocs.io" +Source = "https://github.com/arrow-py/arrow" +Issues = "https://github.com/arrow-py/arrow/issues" + +[tool.flit.module] +name = "arrow" diff --git a/requirements/requirements-tests.txt b/requirements/requirements-tests.txt index 7e9fbe3f..77005e0b 100644 --- a/requirements/requirements-tests.txt +++ b/requirements/requirements-tests.txt @@ -4,7 +4,5 @@ pre-commit pytest pytest-cov pytest-mock -python-dateutil>=2.7.0 pytz==2021.1 simplejson==3.* -typing_extensions; python_version < '3.8' diff --git a/setup.py b/setup.py deleted file mode 100644 index d58b9f3b..00000000 --- a/setup.py +++ /dev/null @@ -1,48 +0,0 @@ -# mypy: ignore-errors -from pathlib import Path - -from setuptools import setup - -readme = Path("README.rst").read_text(encoding="utf-8") -version = Path("arrow/_version.py").read_text(encoding="utf-8") -about = {} -exec(version, about) - -setup( - name="arrow", - version=about["__version__"], - description="Better dates & times for Python", - long_description=readme, - long_description_content_type="text/x-rst", - url="https://arrow.readthedocs.io", - author="Chris Smith", - author_email="crsmithdev@gmail.com", - license="Apache 2.0", - packages=["arrow"], - package_data={"arrow": ["py.typed"]}, - zip_safe=False, - python_requires=">=3.8", - install_requires=[ - "python-dateutil>=2.7.0", - "types-python-dateutil>=2.8.10", - ], - classifiers=[ - "Development Status :: 5 - Production/Stable", - "Intended Audience :: Developers", - "License :: OSI Approved :: Apache Software License", - "Topic :: Software Development :: Libraries :: Python Modules", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", - ], - keywords="arrow date time datetime timestamp timezone humanize", - project_urls={ - "Repository": "https://github.com/arrow-py/arrow", - "Bug Reports": "https://github.com/arrow-py/arrow/issues", - "Documentation": "https://arrow.readthedocs.io", - }, -) diff --git a/tox.ini b/tox.ini index 03c4d2af..c410a8e5 100644 --- a/tox.ini +++ b/tox.ini @@ -36,6 +36,15 @@ commands = doc8 index.rst ../README.rst --extension .rst --ignore D001 make html SPHINXOPTS="-W --keep-going" +[testenv:publish] +passenv = * +skip_install = true +deps = + -r requirements/requirements.txt + flit +allowlist_externals = flit +commands = flit publish --setup-py + [pytest] addopts = -v --cov-branch --cov=arrow --cov-fail-under=99 --cov-report=term-missing --cov-report=xml testpaths = tests From 87a1a774aad0505d9da18ad1d16f6e571f262503 Mon Sep 17 00:00:00 2001 From: Jad Chaar Date: Sat, 30 Sep 2023 15:03:06 -0700 Subject: [PATCH 44/44] Bump version and add changelog --- CHANGELOG.rst | 14 +++++++++++++- arrow/_version.py | 2 +- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 5b079798..b5daf6ed 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,12 +1,24 @@ Changelog ========= +1.3.0 (2023-09-30) +------------------ + +- [ADDED] Added official support for Python 3.11 and 3.12. +- [ADDED] Added dependency on ``types-python-dateutil`` to improve Arrow mypy compatibility. `PR #1102 `_ +- [FIX] Updates to Italian, Romansh, Hungarian, Finish and Arabic locales. +- [FIX] Handling parsing of UTC prefix in timezone strings. +- [CHANGED] Update documentation to improve readability. +- [CHANGED] Dropped support for Python 3.6 and 3.7, which are end-of-life. +- [INTERNAL] Migrate from ``setup.py``/Twine to ``pyproject.toml``/Flit for packaging and distribution. +- [INTERNAL] Adopt ``.readthedocs.yaml`` configuration file for continued ReadTheDocs support. + 1.2.3 (2022-06-25) ------------------ - [NEW] Added Amharic, Armenian, Georgian, Laotian and Uzbek locales. - [FIX] Updated Danish locale and associated tests. -- [INTERNAl] Small fixes to CI. +- [INTERNAL] Small fixes to CI. 1.2.2 (2022-01-19) ------------------ diff --git a/arrow/_version.py b/arrow/_version.py index 10aa336c..67bc602a 100644 --- a/arrow/_version.py +++ b/arrow/_version.py @@ -1 +1 @@ -__version__ = "1.2.3" +__version__ = "1.3.0"