From f4c52093aaef016ddc2b6638b252ce980691dbb5 Mon Sep 17 00:00:00 2001 From: L2501 Date: Sun, 19 Nov 2023 23:09:16 +0200 Subject: [PATCH] [script.module.arrow] 1.2.3 (#2510) --- script.module.arrow/LICENSE.txt | 192 +- script.module.arrow/README.md | 6 - script.module.arrow/addon.xml | 26 +- script.module.arrow/lib/CHANGELOG.rst | 647 ---- script.module.arrow/lib/LICENSE | 201 -- script.module.arrow/lib/README.rst | 134 - script.module.arrow/lib/arrow/_version.py | 2 +- script.module.arrow/lib/arrow/api.py | 19 +- script.module.arrow/lib/arrow/arrow.py | 242 +- script.module.arrow/lib/arrow/constants.py | 145 +- script.module.arrow/lib/arrow/factory.py | 65 +- script.module.arrow/lib/arrow/formatter.py | 5 +- script.module.arrow/lib/arrow/locales.py | 2834 +++++++++++++++--- script.module.arrow/lib/arrow/parser.py | 10 +- script.module.arrow/lib/arrow/py.typed | 0 script.module.arrow/lib/arrow/util.py | 16 +- script.module.arrow/{ => resources}/icon.png | Bin 17 files changed, 3089 insertions(+), 1455 deletions(-) delete mode 100644 script.module.arrow/README.md delete mode 100644 script.module.arrow/lib/CHANGELOG.rst delete mode 100644 script.module.arrow/lib/LICENSE delete mode 100644 script.module.arrow/lib/README.rst delete mode 100644 script.module.arrow/lib/arrow/py.typed rename script.module.arrow/{ => resources}/icon.png (100%) diff --git a/script.module.arrow/LICENSE.txt b/script.module.arrow/LICENSE.txt index 87452e6609..4f9eea5d14 100644 --- a/script.module.arrow/LICENSE.txt +++ b/script.module.arrow/LICENSE.txt @@ -1,4 +1,192 @@ -Copyright 2013 Chris Smith + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2021 Chris Smith Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -10,4 +198,4 @@ Copyright 2013 Chris Smith distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and - limitations under the License. \ No newline at end of file + limitations under the License. diff --git a/script.module.arrow/README.md b/script.module.arrow/README.md deleted file mode 100644 index 3c886e116c..0000000000 --- a/script.module.arrow/README.md +++ /dev/null @@ -1,6 +0,0 @@ -script.module.arrow -====================== - -Python arrow library packed for Kodi. - -See https://github.com/crsmithdev/arrow diff --git a/script.module.arrow/addon.xml b/script.module.arrow/addon.xml index 7befdadebc..1971bec8ca 100644 --- a/script.module.arrow/addon.xml +++ b/script.module.arrow/addon.xml @@ -1,24 +1,20 @@ - + - - - + + + - + - Arrow: better dates and times for Python - Packed for Kodi from https://github.com/crsmithdev/arrow + Better dates and times for Python + Better dates and times for Python + Apache-2.0 all - Apache2 - https://github.com/Razzeee/script.module.arrow - beenje AT gmail.com + https://arrow.readthedocs.io/en/latest/ + https://github.com/arrow-py/arrow - icon.png + resources/icon.png diff --git a/script.module.arrow/lib/CHANGELOG.rst b/script.module.arrow/lib/CHANGELOG.rst deleted file mode 100644 index 300af2089a..0000000000 --- a/script.module.arrow/lib/CHANGELOG.rst +++ /dev/null @@ -1,647 +0,0 @@ -Changelog -========= - -1.0.3 (2021-03-05) ------------------- - -- [FIX] Updated internals to avoid issues when running ``mypy --strict``. -- [FIX] Corrections to Swedish locale. -- [INTERNAL] Lowered required coverage limit until ``humanize`` month tests are fixed. - -1.0.2 (2021-02-28) ------------------- - -- [FIXED] Fixed an ``OverflowError`` that could occur when running Arrow on a 32-bit OS. - -1.0.1 (2021-02-27) ------------------- - -- [FIXED] A ``py.typed`` file is now bundled with the Arrow package to conform to PEP 561. - -1.0.0 (2021-02-26) ------------------- - -After 8 years we're pleased to announce Arrow v1.0. Thanks to the entire Python community for helping make Arrow the amazing package it is today! - -- [CHANGE] Arrow has **dropped support** for Python 2.7 and 3.5. -- [CHANGE] There are multiple **breaking changes** with this release, please see the `migration guide `_ for a complete overview. -- [CHANGE] Arrow is now following `semantic versioning `_. -- [CHANGE] Made ``humanize`` granularity="auto" limits more accurate to reduce strange results. -- [NEW] Added support for Python 3.9. -- [NEW] Added a new keyword argument "exact" to ``span``, ``span_range`` and ``interval`` methods. This makes timespans begin at the start time given and not extend beyond the end time given, for example: - -.. code-block:: python - - >>> start = Arrow(2021, 2, 5, 12, 30) - >>> end = Arrow(2021, 2, 5, 17, 15) - >>> for r in arrow.Arrow.span_range('hour', start, end, exact=True): - ... print(r) - ... - (, ) - (, ) - (, ) - (, ) - (, ) - -- [NEW] Arrow now natively supports PEP 484-style type annotations. -- [FIX] Fixed handling of maximum permitted timestamp on Windows systems. -- [FIX] Corrections to French, German, Japanese and Norwegian locales. -- [INTERNAL] Raise more appropriate errors when string parsing fails to match. - -0.17.0 (2020-10-2) -------------------- - -- [WARN] Arrow will **drop support** for Python 2.7 and 3.5 in the upcoming 1.0.0 release. This is the last major release to support Python 2.7 and Python 3.5. -- [NEW] Arrow now properly handles imaginary datetimes during DST shifts. For example: - -.. code-block:: python - - >>> just_before = arrow.get(2013, 3, 31, 1, 55, tzinfo="Europe/Paris") - >>> just_before.shift(minutes=+10) - - -.. code-block:: python - - >>> before = arrow.get("2018-03-10 23:00:00", "YYYY-MM-DD HH:mm:ss", tzinfo="US/Pacific") - >>> after = arrow.get("2018-03-11 04:00:00", "YYYY-MM-DD HH:mm:ss", tzinfo="US/Pacific") - >>> result=[(t, t.to("utc")) for t in arrow.Arrow.range("hour", before, after)] - >>> for r in result: - ... print(r) - ... - (, ) - (, ) - (, ) - (, ) - (, ) - -- [NEW] Added ``humanize`` week granularity translation for Tagalog. -- [CHANGE] Calls to the ``timestamp`` property now emit a ``DeprecationWarning``. In a future release, ``timestamp`` will be changed to a method to align with Python's datetime module. If you would like to continue using the property, please change your code to use the ``int_timestamp`` or ``float_timestamp`` properties instead. -- [CHANGE] Expanded and improved Catalan locale. -- [FIX] Fixed a bug that caused ``Arrow.range()`` to incorrectly cut off ranges in certain scenarios when using month, quarter, or year endings. -- [FIX] Fixed a bug that caused day of week token parsing to be case sensitive. -- [INTERNAL] A number of functions were reordered in arrow.py for better organization and grouping of related methods. This change will have no impact on usage. -- [INTERNAL] A minimum tox version is now enforced for compatibility reasons. Contributors must use tox >3.18.0 going forward. - -0.16.0 (2020-08-23) -------------------- - -- [WARN] Arrow will **drop support** for Python 2.7 and 3.5 in the upcoming 1.0.0 release. The 0.16.x and 0.17.x releases are the last to support Python 2.7 and 3.5. -- [NEW] Implemented `PEP 495 `_ to handle ambiguous datetimes. This is achieved by the addition of the ``fold`` attribute for Arrow objects. For example: - -.. code-block:: python - - >>> before = Arrow(2017, 10, 29, 2, 0, tzinfo='Europe/Stockholm') - - >>> before.fold - 0 - >>> before.ambiguous - True - >>> after = Arrow(2017, 10, 29, 2, 0, tzinfo='Europe/Stockholm', fold=1) - - >>> after = before.replace(fold=1) - - -- [NEW] Added ``normalize_whitespace`` flag to ``arrow.get``. This is useful for parsing log files and/or any files that may contain inconsistent spacing. For example: - -.. code-block:: python - - >>> arrow.get("Jun 1 2005 1:33PM", "MMM D YYYY H:mmA", normalize_whitespace=True) - - >>> arrow.get("2013-036 \t 04:05:06Z", normalize_whitespace=True) - - -0.15.8 (2020-07-23) -------------------- - -- [WARN] Arrow will **drop support** for Python 2.7 and 3.5 in the upcoming 1.0.0 release. The 0.15.x, 0.16.x, and 0.17.x releases are the last to support Python 2.7 and 3.5. -- [NEW] Added ``humanize`` week granularity translation for Czech. -- [FIX] ``arrow.get`` will now pick sane defaults when weekdays are passed with particular token combinations, see `#446 `_. -- [INTERNAL] Moved arrow to an organization. The repo can now be found `here `_. -- [INTERNAL] Started issuing deprecation warnings for Python 2.7 and 3.5. -- [INTERNAL] Added Python 3.9 to CI pipeline. - -0.15.7 (2020-06-19) -------------------- - -- [NEW] Added a number of built-in format strings. See the `docs `_ for a complete list of supported formats. For example: - -.. code-block:: python - - >>> arw = arrow.utcnow() - >>> arw.format(arrow.FORMAT_COOKIE) - 'Wednesday, 27-May-2020 10:30:35 UTC' - -- [NEW] Arrow is now fully compatible with Python 3.9 and PyPy3. -- [NEW] Added Makefile, tox.ini, and requirements.txt files to the distribution bundle. -- [NEW] Added French Canadian and Swahili locales. -- [NEW] Added ``humanize`` week granularity translation for Hebrew, Greek, Macedonian, Swedish, Slovak. -- [FIX] ms and μs timestamps are now normalized in ``arrow.get()``, ``arrow.fromtimestamp()``, and ``arrow.utcfromtimestamp()``. For example: - -.. code-block:: python - - >>> ts = 1591161115194556 - >>> arw = arrow.get(ts) - - >>> arw.timestamp - 1591161115 - -- [FIX] Refactored and updated Macedonian, Hebrew, Korean, and Portuguese locales. - -0.15.6 (2020-04-29) -------------------- - -- [NEW] Added support for parsing and formatting `ISO 8601 week dates `_ via a new token ``W``, for example: - -.. code-block:: python - - >>> arrow.get("2013-W29-6", "W") - - >>> utc=arrow.utcnow() - >>> utc - - >>> utc.format("W") - '2020-W04-4' - -- [NEW] Formatting with ``x`` token (microseconds) is now possible, for example: - -.. code-block:: python - - >>> dt = arrow.utcnow() - >>> dt.format("x") - '1585669870688329' - >>> dt.format("X") - '1585669870' - -- [NEW] Added ``humanize`` week granularity translation for German, Italian, Polish & Taiwanese locales. -- [FIX] Consolidated and simplified German locales. -- [INTERNAL] Moved testing suite from nosetest/Chai to pytest/pytest-mock. -- [INTERNAL] Converted xunit-style setup and teardown functions in tests to pytest fixtures. -- [INTERNAL] Setup Github Actions for CI alongside Travis. -- [INTERNAL] Help support Arrow's future development by donating to the project on `Open Collective `_. - -0.15.5 (2020-01-03) -------------------- - -- [WARN] Python 2 reached EOL on 2020-01-01. arrow will **drop support** for Python 2 in a future release to be decided (see `#739 `_). -- [NEW] Added bounds parameter to ``span_range``, ``interval`` and ``span`` methods. This allows you to include or exclude the start and end values. -- [NEW] ``arrow.get()`` can now create arrow objects from a timestamp with a timezone, for example: - -.. code-block:: python - - >>> arrow.get(1367900664, tzinfo=tz.gettz('US/Pacific')) - - -- [NEW] ``humanize`` can now combine multiple levels of granularity, for example: - -.. code-block:: python - - >>> later140 = arrow.utcnow().shift(seconds=+8400) - >>> later140.humanize(granularity="minute") - 'in 139 minutes' - >>> later140.humanize(granularity=["hour", "minute"]) - 'in 2 hours and 19 minutes' - -- [NEW] Added Hong Kong locale (``zh_hk``). -- [NEW] Added ``humanize`` week granularity translation for Dutch. -- [NEW] Numbers are now displayed when using the seconds granularity in ``humanize``. -- [CHANGE] ``range`` now supports both the singular and plural forms of the ``frames`` argument (e.g. day and days). -- [FIX] Improved parsing of strings that contain punctuation. -- [FIX] Improved behaviour of ``humanize`` when singular seconds are involved. - -0.15.4 (2019-11-02) -------------------- - -- [FIX] Fixed an issue that caused package installs to fail on Conda Forge. - -0.15.3 (2019-11-02) -------------------- - -- [NEW] ``factory.get()`` can now create arrow objects from a ISO calendar tuple, for example: - -.. code-block:: python - - >>> arrow.get((2013, 18, 7)) - - -- [NEW] Added a new token ``x`` to allow parsing of integer timestamps with milliseconds and microseconds. -- [NEW] Formatting now supports escaping of characters using the same syntax as parsing, for example: - -.. code-block:: python - - >>> arw = arrow.now() - >>> fmt = "YYYY-MM-DD h [h] m" - >>> arw.format(fmt) - '2019-11-02 3 h 32' - -- [NEW] Added ``humanize`` week granularity translations for Chinese, Spanish and Vietnamese. -- [CHANGE] Added ``ParserError`` to module exports. -- [FIX] Added support for midnight at end of day. See `#703 `_ for details. -- [INTERNAL] Created Travis build for macOS. -- [INTERNAL] Test parsing and formatting against full timezone database. - -0.15.2 (2019-09-14) -------------------- - -- [NEW] Added ``humanize`` week granularity translations for Portuguese and Brazilian Portuguese. -- [NEW] Embedded changelog within docs and added release dates to versions. -- [FIX] Fixed a bug that caused test failures on Windows only, see `#668 `_ for details. - -0.15.1 (2019-09-10) -------------------- - -- [NEW] Added ``humanize`` week granularity translations for Japanese. -- [FIX] Fixed a bug that caused Arrow to fail when passed a negative timestamp string. -- [FIX] Fixed a bug that caused Arrow to fail when passed a datetime object with ``tzinfo`` of type ``StaticTzInfo``. - -0.15.0 (2019-09-08) -------------------- - -- [NEW] Added support for DDD and DDDD ordinal date tokens. The following functionality is now possible: ``arrow.get("1998-045")``, ``arrow.get("1998-45", "YYYY-DDD")``, ``arrow.get("1998-045", "YYYY-DDDD")``. -- [NEW] ISO 8601 basic format for dates and times is now supported (e.g. ``YYYYMMDDTHHmmssZ``). -- [NEW] Added ``humanize`` week granularity translations for French, Russian and Swiss German locales. -- [CHANGE] Timestamps of type ``str`` are no longer supported **without a format string** in the ``arrow.get()`` method. This change was made to support the ISO 8601 basic format and to address bugs such as `#447 `_. - -The following will NOT work in v0.15.0: - -.. code-block:: python - - >>> arrow.get("1565358758") - >>> arrow.get("1565358758.123413") - -The following will work in v0.15.0: - -.. code-block:: python - - >>> arrow.get("1565358758", "X") - >>> arrow.get("1565358758.123413", "X") - >>> arrow.get(1565358758) - >>> arrow.get(1565358758.123413) - -- [CHANGE] When a meridian token (a|A) is passed and no meridians are available for the specified locale (e.g. unsupported or untranslated) a ``ParserError`` is raised. -- [CHANGE] The timestamp token (``X``) will now match float timestamps of type ``str``: ``arrow.get(“1565358758.123415”, “X”)``. -- [CHANGE] Strings with leading and/or trailing whitespace will no longer be parsed without a format string. Please see `the docs `_ for ways to handle this. -- [FIX] The timestamp token (``X``) will now only match on strings that **strictly contain integers and floats**, preventing incorrect matches. -- [FIX] Most instances of ``arrow.get()`` returning an incorrect ``Arrow`` object from a partial parsing match have been eliminated. The following issue have been addressed: `#91 `_, `#196 `_, `#396 `_, `#434 `_, `#447 `_, `#456 `_, `#519 `_, `#538 `_, `#560 `_. - -0.14.7 (2019-09-04) -------------------- - -- [CHANGE] ``ArrowParseWarning`` will no longer be printed on every call to ``arrow.get()`` with a datetime string. The purpose of the warning was to start a conversation about the upcoming 0.15.0 changes and we appreciate all the feedback that the community has given us! - -0.14.6 (2019-08-28) -------------------- - -- [NEW] Added support for ``week`` granularity in ``Arrow.humanize()``. For example, ``arrow.utcnow().shift(weeks=-1).humanize(granularity="week")`` outputs "a week ago". This change introduced two new untranslated words, ``week`` and ``weeks``, to all locale dictionaries, so locale contributions are welcome! -- [NEW] Fully translated the Brazilian Portugese locale. -- [CHANGE] Updated the Macedonian locale to inherit from a Slavic base. -- [FIX] Fixed a bug that caused ``arrow.get()`` to ignore tzinfo arguments of type string (e.g. ``arrow.get(tzinfo="Europe/Paris")``). -- [FIX] Fixed a bug that occurred when ``arrow.Arrow()`` was instantiated with a ``pytz`` tzinfo object. -- [FIX] Fixed a bug that caused Arrow to fail when passed a sub-second token, that when rounded, had a value greater than 999999 (e.g. ``arrow.get("2015-01-12T01:13:15.9999995")``). Arrow should now accurately propagate the rounding for large sub-second tokens. - -0.14.5 (2019-08-09) -------------------- - -- [NEW] Added Afrikaans locale. -- [CHANGE] Removed deprecated ``replace`` shift functionality. Users looking to pass plural properties to the ``replace`` function to shift values should use ``shift`` instead. -- [FIX] Fixed bug that occurred when ``factory.get()`` was passed a locale kwarg. - -0.14.4 (2019-07-30) -------------------- - -- [FIX] Fixed a regression in 0.14.3 that prevented a tzinfo argument of type string to be passed to the ``get()`` function. Functionality such as ``arrow.get("2019072807", "YYYYMMDDHH", tzinfo="UTC")`` should work as normal again. -- [CHANGE] Moved ``backports.functools_lru_cache`` dependency from ``extra_requires`` to ``install_requires`` for ``Python 2.7`` installs to fix `#495 `_. - -0.14.3 (2019-07-28) -------------------- - -- [NEW] Added full support for Python 3.8. -- [CHANGE] Added warnings for upcoming factory.get() parsing changes in 0.15.0. Please see `#612 `_ for full details. -- [FIX] Extensive refactor and update of documentation. -- [FIX] factory.get() can now construct from kwargs. -- [FIX] Added meridians to Spanish Locale. - -0.14.2 (2019-06-06) -------------------- - -- [CHANGE] Travis CI builds now use tox to lint and run tests. -- [FIX] Fixed UnicodeDecodeError on certain locales (#600). - -0.14.1 (2019-06-06) -------------------- - -- [FIX] Fixed ``ImportError: No module named 'dateutil'`` (#598). - -0.14.0 (2019-06-06) -------------------- - -- [NEW] Added provisional support for Python 3.8. -- [CHANGE] Removed support for EOL Python 3.4. -- [FIX] Updated setup.py with modern Python standards. -- [FIX] Upgraded dependencies to latest versions. -- [FIX] Enabled flake8 and black on travis builds. -- [FIX] Formatted code using black and isort. - -0.13.2 (2019-05-30) -------------------- - -- [NEW] Add is_between method. -- [FIX] Improved humanize behaviour for near zero durations (#416). -- [FIX] Correct humanize behaviour with future days (#541). -- [FIX] Documentation updates. -- [FIX] Improvements to German Locale. - -0.13.1 (2019-02-17) -------------------- - -- [NEW] Add support for Python 3.7. -- [CHANGE] Remove deprecation decorators for Arrow.range(), Arrow.span_range() and Arrow.interval(), all now return generators, wrap with list() to get old behavior. -- [FIX] Documentation and docstring updates. - -0.13.0 (2019-01-09) -------------------- - -- [NEW] Added support for Python 3.6. -- [CHANGE] Drop support for Python 2.6/3.3. -- [CHANGE] Return generator instead of list for Arrow.range(), Arrow.span_range() and Arrow.interval(). -- [FIX] Make arrow.get() work with str & tzinfo combo. -- [FIX] Make sure special RegEx characters are escaped in format string. -- [NEW] Added support for ZZZ when formatting. -- [FIX] Stop using datetime.utcnow() in internals, use datetime.now(UTC) instead. -- [FIX] Return NotImplemented instead of TypeError in arrow math internals. -- [NEW] Added Estonian Locale. -- [FIX] Small fixes to Greek locale. -- [FIX] TagalogLocale improvements. -- [FIX] Added test requirements to setup. -- [FIX] Improve docs for get, now and utcnow methods. -- [FIX] Correct typo in depreciation warning. - -0.12.1 ------- - -- [FIX] Allow universal wheels to be generated and reliably installed. -- [FIX] Make humanize respect only_distance when granularity argument is also given. - -0.12.0 ------- - -- [FIX] Compatibility fix for Python 2.x - -0.11.0 ------- - -- [FIX] Fix grammar of ArabicLocale -- [NEW] Add Nepali Locale -- [FIX] Fix month name + rename AustriaLocale -> AustrianLocale -- [FIX] Fix typo in Basque Locale -- [FIX] Fix grammar in PortugueseBrazilian locale -- [FIX] Remove pip --user-mirrors flag -- [NEW] Add Indonesian Locale - -0.10.0 ------- - -- [FIX] Fix getattr off by one for quarter -- [FIX] Fix negative offset for UTC -- [FIX] Update arrow.py - -0.9.0 ------ - -- [NEW] Remove duplicate code -- [NEW] Support gnu date iso 8601 -- [NEW] Add support for universal wheels -- [NEW] Slovenian locale -- [NEW] Slovak locale -- [NEW] Romanian locale -- [FIX] respect limit even if end is defined range -- [FIX] Separate replace & shift functions -- [NEW] Added tox -- [FIX] Fix supported Python versions in documentation -- [NEW] Azerbaijani locale added, locale issue fixed in Turkish. -- [FIX] Format ParserError's raise message - -0.8.0 ------ - -- [] - -0.7.1 ------ - -- [NEW] Esperanto locale (batisteo) - -0.7.0 ------ - -- [FIX] Parse localized strings #228 (swistakm) -- [FIX] Modify tzinfo parameter in ``get`` api #221 (bottleimp) -- [FIX] Fix Czech locale (PrehistoricTeam) -- [FIX] Raise TypeError when adding/subtracting non-dates (itsmeolivia) -- [FIX] Fix pytz conversion error (Kudo) -- [FIX] Fix overzealous time truncation in span_range (kdeldycke) -- [NEW] Humanize for time duration #232 (ybrs) -- [NEW] Add Thai locale (sipp11) -- [NEW] Adding Belarusian (be) locale (oire) -- [NEW] Search date in strings (beenje) -- [NEW] Note that arrow's tokens differ from strptime's. (offby1) - -0.6.0 ------ - -- [FIX] Added support for Python 3 -- [FIX] Avoid truncating oversized epoch timestamps. Fixes #216. -- [FIX] Fixed month abbreviations for Ukrainian -- [FIX] Fix typo timezone -- [FIX] A couple of dialect fixes and two new languages -- [FIX] Spanish locale: ``Miercoles`` should have acute accent -- [Fix] Fix Finnish grammar -- [FIX] Fix typo in 'Arrow.floor' docstring -- [FIX] Use read() utility to open README -- [FIX] span_range for week frame -- [NEW] Add minimal support for fractional seconds longer than six digits. -- [NEW] Adding locale support for Marathi (mr) -- [NEW] Add count argument to span method -- [NEW] Improved docs - -0.5.1 - 0.5.4 -------------- - -- [FIX] test the behavior of simplejson instead of calling for_json directly (tonyseek) -- [FIX] Add Hebrew Locale (doodyparizada) -- [FIX] Update documentation location (andrewelkins) -- [FIX] Update setup.py Development Status level (andrewelkins) -- [FIX] Case insensitive month match (cshowe) - -0.5.0 ------ - -- [NEW] struct_time addition. (mhworth) -- [NEW] Version grep (eirnym) -- [NEW] Default to ISO 8601 format (emonty) -- [NEW] Raise TypeError on comparison (sniekamp) -- [NEW] Adding Macedonian(mk) locale (krisfremen) -- [FIX] Fix for ISO seconds and fractional seconds (sdispater) (andrewelkins) -- [FIX] Use correct Dutch wording for "hours" (wbolster) -- [FIX] Complete the list of english locales (indorilftw) -- [FIX] Change README to reStructuredText (nyuszika7h) -- [FIX] Parse lower-cased 'h' (tamentis) -- [FIX] Slight modifications to Dutch locale (nvie) - -0.4.4 ------ - -- [NEW] Include the docs in the released tarball -- [NEW] Czech localization Czech localization for Arrow -- [NEW] Add fa_ir to locales -- [FIX] Fixes parsing of time strings with a final Z -- [FIX] Fixes ISO parsing and formatting for fractional seconds -- [FIX] test_fromtimestamp sp -- [FIX] some typos fixed -- [FIX] removed an unused import statement -- [FIX] docs table fix -- [FIX] Issue with specify 'X' template and no template at all to arrow.get -- [FIX] Fix "import" typo in docs/index.rst -- [FIX] Fix unit tests for zero passed -- [FIX] Update layout.html -- [FIX] In Norwegian and new Norwegian months and weekdays should not be capitalized -- [FIX] Fixed discrepancy between specifying 'X' to arrow.get and specifying no template - -0.4.3 ------ - -- [NEW] Turkish locale (Emre) -- [NEW] Arabic locale (Mosab Ahmad) -- [NEW] Danish locale (Holmars) -- [NEW] Icelandic locale (Holmars) -- [NEW] Hindi locale (Atmb4u) -- [NEW] Malayalam locale (Atmb4u) -- [NEW] Finnish locale (Stormpat) -- [NEW] Portuguese locale (Danielcorreia) -- [NEW] ``h`` and ``hh`` strings are now supported (Averyonghub) -- [FIX] An incorrect inflection in the Polish locale has been fixed (Avalanchy) -- [FIX] ``arrow.get`` now properly handles ``Date`` (Jaapz) -- [FIX] Tests are now declared in ``setup.py`` and the manifest (Pypingou) -- [FIX] ``__version__`` has been added to ``__init__.py`` (Sametmax) -- [FIX] ISO 8601 strings can be parsed without a separator (Ivandiguisto / Root) -- [FIX] Documentation is now more clear regarding some inputs on ``arrow.get`` (Eriktaubeneck) -- [FIX] Some documentation links have been fixed (Vrutsky) -- [FIX] Error messages for parse errors are now more descriptive (Maciej Albin) -- [FIX] The parser now correctly checks for separators in strings (Mschwager) - -0.4.2 ------ - -- [NEW] Factory ``get`` method now accepts a single ``Arrow`` argument. -- [NEW] Tokens SSSS, SSSSS and SSSSSS are supported in parsing. -- [NEW] ``Arrow`` objects have a ``float_timestamp`` property. -- [NEW] Vietnamese locale (Iu1nguoi) -- [NEW] Factory ``get`` method now accepts a list of format strings (Dgilland) -- [NEW] A MANIFEST.in file has been added (Pypingou) -- [NEW] Tests can be run directly from ``setup.py`` (Pypingou) -- [FIX] Arrow docs now list 'day of week' format tokens correctly (Rudolphfroger) -- [FIX] Several issues with the Korean locale have been resolved (Yoloseem) -- [FIX] ``humanize`` now correctly returns unicode (Shvechikov) -- [FIX] ``Arrow`` objects now pickle / unpickle correctly (Yoloseem) - -0.4.1 ------ - -- [NEW] Table / explanation of formatting & parsing tokens in docs -- [NEW] Brazilian locale (Augusto2112) -- [NEW] Dutch locale (OrangeTux) -- [NEW] Italian locale (Pertux) -- [NEW] Austrain locale (LeChewbacca) -- [NEW] Tagalog locale (Marksteve) -- [FIX] Corrected spelling and day numbers in German locale (LeChewbacca) -- [FIX] Factory ``get`` method should now handle unicode strings correctly (Bwells) -- [FIX] Midnight and noon should now parse and format correctly (Bwells) - -0.4.0 ------ - -- [NEW] Format-free ISO 8601 parsing in factory ``get`` method -- [NEW] Support for 'week' / 'weeks' in ``span``, ``range``, ``span_range``, ``floor`` and ``ceil`` -- [NEW] Support for 'weeks' in ``replace`` -- [NEW] Norwegian locale (Martinp) -- [NEW] Japanese locale (CortYuming) -- [FIX] Timezones no longer show the wrong sign when formatted (Bean) -- [FIX] Microseconds are parsed correctly from strings (Bsidhom) -- [FIX] Locale day-of-week is no longer off by one (Cynddl) -- [FIX] Corrected plurals of Ukrainian and Russian nouns (Catchagain) -- [CHANGE] Old 0.1 ``arrow`` module method removed -- [CHANGE] Dropped timestamp support in ``range`` and ``span_range`` (never worked correctly) -- [CHANGE] Dropped parsing of single string as tz string in factory ``get`` method (replaced by ISO 8601) - -0.3.5 ------ - -- [NEW] French locale (Cynddl) -- [NEW] Spanish locale (Slapresta) -- [FIX] Ranges handle multiple timezones correctly (Ftobia) - -0.3.4 ------ - -- [FIX] Humanize no longer sometimes returns the wrong month delta -- [FIX] ``__format__`` works correctly with no format string - -0.3.3 ------ - -- [NEW] Python 2.6 support -- [NEW] Initial support for locale-based parsing and formatting -- [NEW] ArrowFactory class, now proxied as the module API -- [NEW] ``factory`` api method to obtain a factory for a custom type -- [FIX] Python 3 support and tests completely ironed out - -0.3.2 ------ - -- [NEW] Python 3+ support - -0.3.1 ------ - -- [FIX] The old ``arrow`` module function handles timestamps correctly as it used to - -0.3.0 ------ - -- [NEW] ``Arrow.replace`` method -- [NEW] Accept timestamps, datetimes and Arrows for datetime inputs, where reasonable -- [FIX] ``range`` and ``span_range`` respect end and limit parameters correctly -- [CHANGE] Arrow objects are no longer mutable -- [CHANGE] Plural attribute name semantics altered: single -> absolute, plural -> relative -- [CHANGE] Plural names no longer supported as properties (e.g. ``arrow.utcnow().years``) - -0.2.1 ------ - -- [NEW] Support for localized humanization -- [NEW] English, Russian, Greek, Korean, Chinese locales - -0.2.0 ------ - -- **REWRITE** -- [NEW] Date parsing -- [NEW] Date formatting -- [NEW] ``floor``, ``ceil`` and ``span`` methods -- [NEW] ``datetime`` interface implementation -- [NEW] ``clone`` method -- [NEW] ``get``, ``now`` and ``utcnow`` API methods - -0.1.6 ------ - -- [NEW] Humanized time deltas -- [NEW] ``__eq__`` implemented -- [FIX] Issues with conversions related to daylight savings time resolved -- [CHANGE] ``__str__`` uses ISO formatting - -0.1.5 ------ - -- **Started tracking changes** -- [NEW] Parsing of ISO-formatted time zone offsets (e.g. '+02:30', '-05:00') -- [NEW] Resolved some issues with timestamps and delta / Olson time zones diff --git a/script.module.arrow/lib/LICENSE b/script.module.arrow/lib/LICENSE deleted file mode 100644 index 4f9eea5d14..0000000000 --- a/script.module.arrow/lib/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2021 Chris Smith - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/script.module.arrow/lib/README.rst b/script.module.arrow/lib/README.rst deleted file mode 100644 index abdf755606..0000000000 --- a/script.module.arrow/lib/README.rst +++ /dev/null @@ -1,134 +0,0 @@ -Arrow: Better dates & times for Python -====================================== - -.. start-inclusion-marker-do-not-remove - -.. image:: https://github.com/arrow-py/arrow/workflows/tests/badge.svg?branch=master - :alt: Build Status - :target: https://github.com/arrow-py/arrow/actions?query=workflow%3Atests+branch%3Amaster - -.. image:: https://codecov.io/gh/arrow-py/arrow/branch/master/graph/badge.svg - :alt: Coverage - :target: https://codecov.io/gh/arrow-py/arrow - -.. image:: https://img.shields.io/pypi/v/arrow.svg - :alt: PyPI Version - :target: https://pypi.python.org/pypi/arrow - -.. image:: https://img.shields.io/pypi/pyversions/arrow.svg - :alt: Supported Python Versions - :target: https://pypi.python.org/pypi/arrow - -.. image:: https://img.shields.io/pypi/l/arrow.svg - :alt: License - :target: https://pypi.python.org/pypi/arrow - -.. image:: https://img.shields.io/badge/code%20style-black-000000.svg - :alt: Code Style: Black - :target: https://github.com/psf/black - - -**Arrow** is a Python library that offers a sensible and human-friendly approach to creating, manipulating, formatting and converting dates, times and timestamps. It implements and updates the datetime type, plugging gaps in functionality and providing an intelligent module API that supports many common creation scenarios. Simply put, it helps you work with dates and times with fewer imports and a lot less code. - -Arrow is named after the `arrow of time `_ and is heavily inspired by `moment.js `_ and `requests `_. - -Why use Arrow over built-in modules? ------------------------------------- - -Python's standard library and some other low-level modules have near-complete date, time and timezone functionality, but don't work very well from a usability perspective: - -- Too many modules: datetime, time, calendar, dateutil, pytz and more -- Too many types: date, time, datetime, tzinfo, timedelta, relativedelta, etc. -- Timezones and timestamp conversions are verbose and unpleasant -- Timezone naivety is the norm -- Gaps in functionality: ISO 8601 parsing, timespans, humanization - -Features --------- - -- Fully-implemented, drop-in replacement for datetime -- Support for Python 3.6+ -- Timezone-aware and UTC by default -- Super-simple creation options for many common input scenarios -- ``shift`` method with support for relative offsets, including weeks -- Format and parse strings automatically -- Wide support for the `ISO 8601 `_ standard -- Timezone conversion -- Support for ``dateutil``, ``pytz``, and ``ZoneInfo`` tzinfo objects -- Generates time spans, ranges, floors and ceilings for time frames ranging from microsecond to year -- Humanize dates and times with a growing list of contributed locales -- Extensible for your own Arrow-derived types -- Full support for PEP 484-style type hints - -Quick Start ------------ - -Installation -~~~~~~~~~~~~ - -To install Arrow, use `pip `_ or `pipenv `_: - -.. code-block:: console - - $ pip install -U arrow - -Example Usage -~~~~~~~~~~~~~ - -.. code-block:: python - - >>> import arrow - >>> arrow.get('2013-05-11T21:23:58.970460+07:00') - - - >>> utc = arrow.utcnow() - >>> utc - - - >>> utc = utc.shift(hours=-1) - >>> utc - - - >>> local = utc.to('US/Pacific') - >>> local - - - >>> local.timestamp() - 1368303838.970460 - - >>> local.format() - '2013-05-11 13:23:58 -07:00' - - >>> local.format('YYYY-MM-DD HH:mm:ss ZZ') - '2013-05-11 13:23:58 -07:00' - - >>> local.humanize() - 'an hour ago' - - >>> local.humanize(locale='ko_kr') - '1시간 전' - -.. end-inclusion-marker-do-not-remove - -Documentation -------------- - -For full documentation, please visit `arrow.readthedocs.io `_. - -Contributing ------------- - -Contributions are welcome for both code and localizations (adding and updating locales). Begin by gaining familiarity with the Arrow library and its features. Then, jump into contributing: - -#. Find an issue or feature to tackle on the `issue tracker `_. Issues marked with the `"good first issue" label `_ may be a great place to start! -#. Fork `this repository `_ on GitHub and begin making changes in a branch. -#. Add a few tests to ensure that the bug was fixed or the feature works as expected. -#. Run the entire test suite and linting checks by running one of the following commands: ``tox && tox -e lint,docs`` (if you have `tox `_ installed) **OR** ``make build39 && make test && make lint`` (if you do not have Python 3.9 installed, replace ``build39`` with the latest Python version on your system). -#. Submit a pull request and await feedback 😃. - -If you have any questions along the way, feel free to ask them `here `_. - -Support Arrow -------------- - -`Open Collective `_ is an online funding platform that provides tools to raise money and share your finances with full transparency. It is the platform of choice for individuals and companies to make one-time or recurring donations directly to the project. If you are interested in making a financial contribution, please visit the `Arrow collective `_. diff --git a/script.module.arrow/lib/arrow/_version.py b/script.module.arrow/lib/arrow/_version.py index 976498ab9c..10aa336ce0 100644 --- a/script.module.arrow/lib/arrow/_version.py +++ b/script.module.arrow/lib/arrow/_version.py @@ -1 +1 @@ -__version__ = "1.0.3" +__version__ = "1.2.3" diff --git a/script.module.arrow/lib/arrow/api.py b/script.module.arrow/lib/arrow/api.py index 95696f3c15..d8ed24b978 100644 --- a/script.module.arrow/lib/arrow/api.py +++ b/script.module.arrow/lib/arrow/api.py @@ -10,6 +10,7 @@ from typing import Any, List, Optional, Tuple, Type, Union, overload from arrow.arrow import TZ_EXPR, Arrow +from arrow.constants import DEFAULT_LOCALE from arrow.factory import ArrowFactory # internal default factory. @@ -22,7 +23,17 @@ @overload def get( *, - locale: str = "en_us", + locale: str = DEFAULT_LOCALE, + tzinfo: Optional[TZ_EXPR] = None, + normalize_whitespace: bool = False, +) -> Arrow: + ... # pragma: no cover + + +@overload +def get( + *args: int, + locale: str = DEFAULT_LOCALE, tzinfo: Optional[TZ_EXPR] = None, normalize_whitespace: bool = False, ) -> Arrow: @@ -43,7 +54,7 @@ def get( Tuple[int, int, int], ], *, - locale: str = "en_us", + locale: str = DEFAULT_LOCALE, tzinfo: Optional[TZ_EXPR] = None, normalize_whitespace: bool = False, ) -> Arrow: @@ -55,7 +66,7 @@ def get( __arg1: Union[datetime, date], __arg2: TZ_EXPR, *, - locale: str = "en_us", + locale: str = DEFAULT_LOCALE, tzinfo: Optional[TZ_EXPR] = None, normalize_whitespace: bool = False, ) -> Arrow: @@ -67,7 +78,7 @@ def get( __arg1: str, __arg2: Union[str, List[str]], *, - locale: str = "en_us", + locale: str = DEFAULT_LOCALE, tzinfo: Optional[TZ_EXPR] = None, normalize_whitespace: bool = False, ) -> Arrow: diff --git a/script.module.arrow/lib/arrow/arrow.py b/script.module.arrow/lib/arrow/arrow.py index dda7b40562..1ede107f56 100644 --- a/script.module.arrow/lib/arrow/arrow.py +++ b/script.module.arrow/lib/arrow/arrow.py @@ -6,6 +6,7 @@ import calendar +import re import sys from datetime import date from datetime import datetime as dt_datetime @@ -32,6 +33,7 @@ from dateutil.relativedelta import relativedelta from arrow import formatter, locales, parser, util +from arrow.constants import DEFAULT_LOCALE, DEHUMANIZE_LOCALES from arrow.locales import TimeFrameLiteral if sys.version_info < (3, 8): # pragma: no cover @@ -73,6 +75,7 @@ "day", "week", "month", + "quarter", "year", ] @@ -91,7 +94,7 @@ class Arrow: :param second: (optional) the second, Defaults to 0. :param microsecond: (optional) the microsecond. Defaults to 0. :param tzinfo: (optional) A timezone expression. Defaults to UTC. - :param fold: (optional) 0 or 1, used to disambiguate repeated times. Defaults to 0. + :param fold: (optional) 0 or 1, used to disambiguate repeated wall times. Defaults to 0. .. _tz-expr: @@ -130,6 +133,7 @@ class Arrow: _SECS_PER_DAY: Final[int] = 60 * 60 * 24 _SECS_PER_WEEK: Final[int] = 60 * 60 * 24 * 7 _SECS_PER_MONTH: Final[float] = 60 * 60 * 24 * 30.5 + _SECS_PER_QUARTER: Final[float] = 60 * 60 * 24 * 30.5 * 3 _SECS_PER_YEAR: Final[int] = 60 * 60 * 24 * 365 _SECS_MAP: Final[Mapping[TimeFrameLiteral, float]] = { @@ -139,6 +143,7 @@ class Arrow: "day": _SECS_PER_DAY, "week": _SECS_PER_WEEK, "month": _SECS_PER_MONTH, + "quarter": _SECS_PER_QUARTER, "year": _SECS_PER_YEAR, } @@ -245,6 +250,7 @@ def fromtimestamp( :param timestamp: an ``int`` or ``float`` timestamp, or a ``str`` that converts to either. :param tzinfo: (optional) a ``tzinfo`` object. Defaults to local time. + """ if tzinfo is None: @@ -305,12 +311,12 @@ def fromdatetime(cls, dt: dt_datetime, tzinfo: Optional[TZ_EXPR] = None) -> "Arr :param tzinfo: (optional) A :ref:`timezone expression `. Defaults to ``dt``'s timezone, or UTC if naive. - If you only want to replace the timezone of naive datetimes:: + Usage:: >>> dt - datetime.datetime(2013, 5, 5, 0, 0, tzinfo=tzutc()) - >>> arrow.Arrow.fromdatetime(dt, dt.tzinfo or 'US/Pacific') - + datetime.datetime(2021, 4, 7, 13, 48, tzinfo=tzfile('/usr/share/zoneinfo/US/Pacific')) + >>> arrow.Arrow.fromdatetime(dt) + """ @@ -335,10 +341,11 @@ def fromdatetime(cls, dt: dt_datetime, tzinfo: Optional[TZ_EXPR] = None) -> "Arr @classmethod def fromdate(cls, date: date, tzinfo: Optional[TZ_EXPR] = None) -> "Arrow": """Constructs an :class:`Arrow ` object from a ``date`` and optional - replacement timezone. Time values are set to 0. + replacement timezone. All time values are set to 0. :param date: the ``date`` :param tzinfo: (optional) A :ref:`timezone expression `. Defaults to UTC. + """ if tzinfo is None: @@ -354,7 +361,7 @@ def strptime( in the style of ``datetime.strptime``. Optionally replaces the parsed timezone. :param date_str: the date string. - :param fmt: the format string. + :param fmt: the format string using datetime format codes. :param tzinfo: (optional) A :ref:`timezone expression `. Defaults to the parsed timezone if ``fmt`` contains a timezone directive, otherwise UTC. @@ -438,7 +445,7 @@ def range( iterating. As such, either call with naive objects and ``tz``, or aware objects from the same timezone and no ``tz``. - Supported frame values: year, quarter, month, week, day, hour, minute, second. + Supported frame values: year, quarter, month, week, day, hour, minute, second, microsecond. Recognized datetime expressions: @@ -504,8 +511,9 @@ def span( count: int = 1, bounds: _BOUNDS = "[)", exact: bool = False, + week_start: int = 1, ) -> Tuple["Arrow", "Arrow"]: - """Returns two new :class:`Arrow ` objects, representing the timespan + """Returns a tuple of two new :class:`Arrow ` objects, representing the timespan of the :class:`Arrow ` object in a given timeframe. :param frame: the timeframe. Can be any ``datetime`` property (day, hour, minute...). @@ -517,6 +525,8 @@ def span( :param exact: (optional) whether to have the start of the timespan begin exactly at the time specified by ``start`` and the end of the timespan truncated so as not to extend beyond ``end``. + :param week_start: (optional) only used in combination with the week timeframe. Follows isoweekday() where + Monday is 1 and Sunday is 7. Supported frame values: year, quarter, month, week, day, hour, minute, second. @@ -537,7 +547,15 @@ def span( >>> arrow.utcnow().span('day', bounds='[]') (, ) + >>> arrow.utcnow().span('week') + (, ) + + >>> arrow.utcnow().span('week', week_start=6) + (, ) + """ + if not 1 <= week_start <= 7: + raise ValueError("week_start argument must be between 1 and 7.") util.validate_bounds(bounds) @@ -563,7 +581,9 @@ def span( floor = self.__class__(*values, tzinfo=self.tzinfo) # type: ignore if frame_absolute == "week": - floor = floor.shift(days=-(self.isoweekday() - 1)) + # if week_start is greater than self.isoweekday() go back one week by setting delta = 7 + delta = 7 if week_start > self.isoweekday() else 0 + floor = floor.shift(days=-(self.isoweekday() - week_start) - delta) elif frame_absolute == "quarter": floor = floor.shift(months=-((self.month - 1) % 3)) @@ -589,6 +609,7 @@ def floor(self, frame: _T_FRAMES) -> "Arrow": >>> arrow.utcnow().floor('hour') + """ return self.span(frame)[0] @@ -605,6 +626,7 @@ def ceil(self, frame: _T_FRAMES) -> "Arrow": >>> arrow.utcnow().ceil('hour') + """ return self.span(frame)[1] @@ -645,7 +667,7 @@ def span_range( iterating. As such, either call with naive objects and ``tz``, or aware objects from the same timezone and no ``tz``. - Supported frame values: year, quarter, month, week, day, hour, minute, second. + Supported frame values: year, quarter, month, week, day, hour, minute, second, microsecond. Recognized datetime expressions: @@ -737,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) ... (, ) (, ) @@ -849,8 +871,8 @@ def timestamp(self) -> float: Usage:: - >>> arrow.utcnow().timestamp - 1548260567 + >>> arrow.utcnow().timestamp() + 1616882340.256501 """ @@ -858,7 +880,7 @@ def timestamp(self) -> float: @property def int_timestamp(self) -> int: - """Returns a timestamp representation of the :class:`Arrow ` object, in + """Returns an integer timestamp representation of the :class:`Arrow ` object, in UTC time. Usage:: @@ -872,7 +894,7 @@ def int_timestamp(self) -> int: @property def float_timestamp(self) -> float: - """Returns a floating-point representation of the :class:`Arrow ` + """Returns a floating-point timestamp representation of the :class:`Arrow ` object, in UTC time. Usage:: @@ -886,13 +908,16 @@ def float_timestamp(self) -> float: @property def fold(self) -> int: - """ Returns the ``fold`` value of the :class:`Arrow ` object. """ + """Returns the ``fold`` value of the :class:`Arrow ` object.""" return self._datetime.fold @property def ambiguous(self) -> bool: - """ Returns a boolean indicating whether the :class:`Arrow ` object is ambiguous.""" + """Indicates whether the :class:`Arrow ` object is a repeated wall time in the current + timezone. + + """ return dateutil_tz.datetime_ambiguous(self._datetime) @@ -1067,9 +1092,11 @@ def to(self, tz: TZ_EXPR) -> "Arrow": # string output and formatting - def format(self, fmt: str = "YYYY-MM-DD HH:mm:ssZZ", locale: str = "en_us") -> str: + def format( + self, fmt: str = "YYYY-MM-DD HH:mm:ssZZ", locale: str = DEFAULT_LOCALE + ) -> str: """Returns a string representation of the :class:`Arrow ` object, - formatted according to a format string. + formatted according to the provided format string. :param fmt: the format string. :param locale: the locale to format. @@ -1095,7 +1122,7 @@ def format(self, fmt: str = "YYYY-MM-DD HH:mm:ssZZ", locale: str = "en_us") -> s def humanize( self, other: Union["Arrow", dt_datetime, None] = None, - locale: str = "en_us", + locale: str = DEFAULT_LOCALE, only_distance: bool = False, granularity: Union[_GRANULARITY, List[_GRANULARITY]] = "auto", ) -> str: @@ -1103,7 +1130,7 @@ def humanize( :param other: (optional) an :class:`Arrow ` or ``datetime`` object. Defaults to now in the current :class:`Arrow ` object's timezone. - :param locale: (optional) a ``str`` specifying a locale. Defaults to 'en_us'. + :param locale: (optional) a ``str`` specifying a locale. Defaults to 'en-us'. :param only_distance: (optional) returns only time difference eg: "11 seconds" without "in" or "ago" part. :param granularity: (optional) defines the precision of the output. Set it to strings 'second', 'minute', 'hour', 'day', 'week', 'month' or 'year' or a list of any combination of these strings @@ -1221,12 +1248,14 @@ def humanize( delta = sign * delta_second / self._SECS_PER_WEEK elif granularity == "month": delta = sign * delta_second / self._SECS_PER_MONTH + elif granularity == "quarter": + delta = sign * delta_second / self._SECS_PER_QUARTER elif granularity == "year": delta = sign * delta_second / self._SECS_PER_YEAR else: raise ValueError( "Invalid level of granularity. " - "Please select between 'second', 'minute', 'hour', 'day', 'week', 'month' or 'year'." + "Please select between 'second', 'minute', 'hour', 'day', 'week', 'month', 'quarter' or 'year'." ) if trunc(abs(delta)) != 1: @@ -1234,6 +1263,13 @@ def humanize( return locale.describe(granularity, delta, only_distance=only_distance) else: + + if not granularity: + raise ValueError( + "Empty granularity list provided. " + "Please select one or more from 'second', 'minute', 'hour', 'day', 'week', 'month', 'quarter', 'year'." + ) + timeframes: List[Tuple[TimeFrameLiteral, float]] = [] def gather_timeframes(_delta: float, _frame: TimeFrameLiteral) -> float: @@ -1251,6 +1287,7 @@ def gather_timeframes(_delta: float, _frame: TimeFrameLiteral) -> float: delta = float(delta_second) frames: Tuple[TimeFrameLiteral, ...] = ( "year", + "quarter", "month", "week", "day", @@ -1264,7 +1301,7 @@ def gather_timeframes(_delta: float, _frame: TimeFrameLiteral) -> float: if len(timeframes) < len(granularity): raise ValueError( "Invalid level of granularity. " - "Please select between 'second', 'minute', 'hour', 'day', 'week', 'month' or 'year'." + "Please select between 'second', 'minute', 'hour', 'day', 'week', 'month', 'quarter' or 'year'." ) return locale.describe_multi(timeframes, only_distance=only_distance) @@ -1275,6 +1312,147 @@ def gather_timeframes(_delta: float, _frame: TimeFrameLiteral) -> float: "Please consider making a contribution to this locale." ) + def dehumanize(self, input_string: str, locale: str = "en_us") -> "Arrow": + """Returns a new :class:`Arrow ` object, that represents + the time difference relative to the attrbiutes of the + :class:`Arrow ` object. + + :param timestring: a ``str`` representing a humanized relative time. + :param locale: (optional) a ``str`` specifying a locale. Defaults to 'en-us'. + + Usage:: + + >>> arw = arrow.utcnow() + >>> arw + + >>> earlier = arw.dehumanize("2 days ago") + >>> earlier + + + >>> arw = arrow.utcnow() + >>> arw + + >>> later = arw.dehumanize("in a month") + >>> later + + + """ + + # Create a locale object based off given local + locale_obj = locales.get_locale(locale) + + # Check to see if locale is supported + normalized_locale_name = locale.lower().replace("_", "-") + + if normalized_locale_name not in DEHUMANIZE_LOCALES: + raise ValueError( + f"Dehumanize does not currently support the {locale} locale, please consider making a contribution to add support for this locale." + ) + + current_time = self.fromdatetime(self._datetime) + + # Create an object containing the relative time info + time_object_info = dict.fromkeys( + ["seconds", "minutes", "hours", "days", "weeks", "months", "years"], 0 + ) + + # Create an object representing if unit has been seen + unit_visited = dict.fromkeys( + ["now", "seconds", "minutes", "hours", "days", "weeks", "months", "years"], + False, + ) + + # Create a regex pattern object for numbers + num_pattern = re.compile(r"\d+") + + # Search input string for each time unit within locale + for unit, unit_object in locale_obj.timeframes.items(): + + # Need to check the type of unit_object to create the correct dictionary + if isinstance(unit_object, Mapping): + strings_to_search = unit_object + else: + strings_to_search = {unit: str(unit_object)} + + # Search for any matches that exist for that locale's unit. + # Needs to cycle all through strings as some locales have strings that + # could overlap in a regex match, since input validation isn't being performed. + for time_delta, time_string in strings_to_search.items(): + + # Replace {0} with regex \d representing digits + search_string = str(time_string) + search_string = search_string.format(r"\d+") + + # Create search pattern and find within string + pattern = re.compile(rf"(^|\b|\d){search_string}") + match = pattern.search(input_string) + + # If there is no match continue to next iteration + if not match: + continue + + match_string = match.group() + num_match = num_pattern.search(match_string) + + # If no number matches + # Need for absolute value as some locales have signs included in their objects + if not num_match: + change_value = ( + 1 if not time_delta.isnumeric() else abs(int(time_delta)) + ) + else: + change_value = int(num_match.group()) + + # No time to update if now is the unit + if unit == "now": + unit_visited[unit] = True + continue + + # Add change value to the correct unit (incorporates the plurality that exists within timeframe i.e second v.s seconds) + time_unit_to_change = str(unit) + time_unit_to_change += ( + "s" if (str(time_unit_to_change)[-1] != "s") else "" + ) + time_object_info[time_unit_to_change] = change_value + unit_visited[time_unit_to_change] = True + + # Assert error if string does not modify any units + if not any([True for k, v in unit_visited.items() if v]): + raise ValueError( + "Input string not valid. Note: Some locales do not support the week granulairty in Arrow. " + "If you are attempting to use the week granularity on an unsupported locale, this could be the cause of this error." + ) + + # Sign logic + future_string = locale_obj.future + future_string = future_string.format(".*") + future_pattern = re.compile(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(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 + # was visited before raising a ValueError + if past_pattern_match: + sign_val = -1 + elif future_pattern_match: + sign_val = 1 + elif unit_visited["now"]: + sign_val = 0 + else: + raise ValueError( + "Invalid input String. String does not contain any relative time information. " + "String should either represent a time in the future or a time in the past. " + "Ex: 'in 5 seconds' or '5 seconds ago'." + ) + + time_changes = {k: sign_val * v for k, v in time_object_info.items()} + + return current_time.shift(**time_changes) + # query functions def is_between( @@ -1283,8 +1461,8 @@ def is_between( end: "Arrow", bounds: _BOUNDS = "()", ) -> bool: - """Returns a boolean denoting whether the specified date and time is between - the start and end dates and times. + """Returns a boolean denoting whether the :class:`Arrow ` object is between + the start and end limits. :param start: an :class:`Arrow ` object. :param end: an :class:`Arrow ` object. @@ -1622,10 +1800,9 @@ def __le__(self, other: Any) -> bool: return self._datetime <= self._get_datetime(other) # internal methods - @staticmethod def _get_tzinfo(tz_expr: Optional[TZ_EXPR]) -> dt_tzinfo: - + """Get normalized tzinfo object from various inputs.""" if tz_expr is None: return dateutil_tz.tzutc() if isinstance(tz_expr, dt_tzinfo): @@ -1640,7 +1817,7 @@ def _get_tzinfo(tz_expr: Optional[TZ_EXPR]) -> dt_tzinfo: def _get_datetime( cls, expr: Union["Arrow", dt_datetime, int, float, str] ) -> dt_datetime: - """Get datetime object for a specified expression.""" + """Get datetime object from a specified expression.""" if isinstance(expr, Arrow): return expr.datetime elif isinstance(expr, dt_datetime): @@ -1653,7 +1830,11 @@ def _get_datetime( @classmethod def _get_frames(cls, name: _T_FRAMES) -> Tuple[str, str, int]: + """Finds relevant timeframe and steps for use in range and span methods. + + Returns a 3 element tuple in the form (frame, plural frame, step), for example ("day", "days", 1) + """ if name in cls._ATTRS: return name, f"{name}s", 1 elif name[-1] == "s" and name[:-1] in cls._ATTRS: @@ -1682,7 +1863,7 @@ def _get_frames(cls, name: _T_FRAMES) -> Tuple[str, str, int]: @classmethod def _get_iteration_params(cls, end: Any, limit: Optional[int]) -> Tuple[Any, int]: - + """Sets default end and limit values for range method.""" if end is None: if limit is None: @@ -1697,6 +1878,7 @@ def _get_iteration_params(cls, end: Any, limit: Optional[int]) -> Tuple[Any, int @staticmethod def _is_last_day_of_month(date: "Arrow") -> bool: + """Returns a boolean indicating whether the datetime is the last day of the month.""" return date.day == calendar.monthrange(date.year, date.month)[1] diff --git a/script.module.arrow/lib/arrow/constants.py b/script.module.arrow/lib/arrow/constants.py index 22ffe55dcc..53d163b999 100644 --- a/script.module.arrow/lib/arrow/constants.py +++ b/script.module.arrow/lib/arrow/constants.py @@ -1,3 +1,5 @@ +"""Constants used internally in arrow.""" + import sys from datetime import datetime @@ -19,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 @@ -32,3 +34,144 @@ MAX_ORDINAL: Final[int] = datetime.max.toordinal() MIN_ORDINAL: Final[int] = 1 + +DEFAULT_LOCALE: Final[str] = "en-us" + +# Supported dehumanize locales +DEHUMANIZE_LOCALES = { + "en", + "en-us", + "en-gb", + "en-au", + "en-be", + "en-jp", + "en-za", + "en-ca", + "en-ph", + "fr", + "fr-fr", + "fr-ca", + "it", + "it-it", + "es", + "es-es", + "el", + "el-gr", + "ja", + "ja-jp", + "se", + "se-fi", + "se-no", + "se-se", + "sv", + "sv-se", + "fi", + "fi-fi", + "zh", + "zh-cn", + "zh-tw", + "zh-hk", + "nl", + "nl-nl", + "be", + "be-by", + "pl", + "pl-pl", + "ru", + "ru-ru", + "af", + "bg", + "bg-bg", + "ua", + "uk", + "uk-ua", + "mk", + "mk-mk", + "de", + "de-de", + "de-ch", + "de-at", + "nb", + "nb-no", + "nn", + "nn-no", + "pt", + "pt-pt", + "pt-br", + "tl", + "tl-ph", + "vi", + "vi-vn", + "tr", + "tr-tr", + "az", + "az-az", + "da", + "da-dk", + "ml", + "hi", + "cs", + "cs-cz", + "sk", + "sk-sk", + "fa", + "fa-ir", + "mr", + "ca", + "ca-es", + "ca-ad", + "ca-fr", + "ca-it", + "eo", + "eo-xx", + "bn", + "bn-bd", + "bn-in", + "rm", + "rm-ch", + "ro", + "ro-ro", + "sl", + "sl-si", + "id", + "id-id", + "ne", + "ne-np", + "ee", + "et", + "sw", + "sw-ke", + "sw-tz", + "la", + "la-va", + "lt", + "lt-lt", + "ms", + "ms-my", + "ms-bn", + "or", + "or-in", + "lb", + "lb-lu", + "zu", + "zu-za", + "sq", + "sq-al", + "ta", + "ta-in", + "ta-lk", + "ur", + "ur-pk", + "ka", + "ka-ge", + "kk", + "kk-kz", + # "lo", + # "lo-la", + "am", + "am-et", + "hy-am", + "hy", + "uz", + "uz-uz", +} diff --git a/script.module.arrow/lib/arrow/factory.py b/script.module.arrow/lib/arrow/factory.py index 231510f585..aad4af8bde 100644 --- a/script.module.arrow/lib/arrow/factory.py +++ b/script.module.arrow/lib/arrow/factory.py @@ -9,6 +9,7 @@ import calendar from datetime import date, datetime from datetime import tzinfo as dt_tzinfo +from decimal import Decimal from time import struct_time from typing import Any, List, Optional, Tuple, Type, Union, overload @@ -16,6 +17,7 @@ from arrow import parser from arrow.arrow import TZ_EXPR, Arrow +from arrow.constants import DEFAULT_LOCALE from arrow.util import is_timestamp, iso_to_gregorian @@ -36,7 +38,7 @@ def __init__(self, type: Type[Arrow] = Arrow) -> None: def get( self, *, - locale: str = "en_us", + locale: str = DEFAULT_LOCALE, tzinfo: Optional[TZ_EXPR] = None, normalize_whitespace: bool = False, ) -> Arrow: @@ -57,7 +59,7 @@ def get( Tuple[int, int, int], ], *, - locale: str = "en_us", + locale: str = DEFAULT_LOCALE, tzinfo: Optional[TZ_EXPR] = None, normalize_whitespace: bool = False, ) -> Arrow: @@ -69,7 +71,7 @@ def get( __arg1: Union[datetime, date], __arg2: TZ_EXPR, *, - locale: str = "en_us", + locale: str = DEFAULT_LOCALE, tzinfo: Optional[TZ_EXPR] = None, normalize_whitespace: bool = False, ) -> Arrow: @@ -81,7 +83,7 @@ def get( __arg1: str, __arg2: Union[str, List[str]], *, - locale: str = "en_us", + locale: str = DEFAULT_LOCALE, tzinfo: Optional[TZ_EXPR] = None, normalize_whitespace: bool = False, ) -> Arrow: @@ -90,7 +92,7 @@ def get( def get(self, *args: Any, **kwargs: Any) -> Arrow: """Returns an :class:`Arrow ` object based on flexible inputs. - :param locale: (optional) a ``str`` specifying a locale for the parser. Defaults to 'en_us'. + :param locale: (optional) a ``str`` specifying a locale for the parser. Defaults to 'en-us'. :param tzinfo: (optional) a :ref:`timezone expression ` or tzinfo object. Replaces the timezone unless using an input form that is explicitly UTC or specifies the timezone in a positional argument. Defaults to UTC. @@ -107,11 +109,6 @@ def get(self, *args: Any, **kwargs: Any) -> Arrow: >>> arrow.get() - **None** to also get current UTC time:: - - >>> arrow.get(None) - - **One** :class:`Arrow ` object, to get a copy. >>> arw = arrow.utcnow() @@ -189,7 +186,7 @@ def get(self, *args: Any, **kwargs: Any) -> Arrow: >>> arrow.get('2013-05-05 12:30:45', ['MM/DD/YYYY', 'YYYY-MM-DD HH:mm:ss']) - **Three or more** arguments, as for the constructor of a ``datetime``:: + **Three or more** arguments, as for the direct constructor of an ``Arrow`` object:: >>> arrow.get(2013, 5, 5, 12, 30, 45) @@ -197,7 +194,7 @@ def get(self, *args: Any, **kwargs: Any) -> Arrow: """ arg_count = len(args) - locale = kwargs.pop("locale", "en_us") + locale = kwargs.pop("locale", DEFAULT_LOCALE) tz = kwargs.get("tzinfo", None) normalize_whitespace = kwargs.pop("normalize_whitespace", False) @@ -209,60 +206,62 @@ def get(self, *args: Any, **kwargs: Any) -> Arrow: if len(kwargs) == 1 and tz is None: arg_count = 3 - # () -> now, @ utc. + # () -> now, @ tzinfo or utc if arg_count == 0: if isinstance(tz, str): tz = parser.TzinfoParser.parse(tz) - return self.type.now(tz) + return self.type.now(tzinfo=tz) if isinstance(tz, dt_tzinfo): - return self.type.now(tz) + return self.type.now(tzinfo=tz) return self.type.utcnow() if arg_count == 1: arg = args[0] + if isinstance(arg, Decimal): + arg = float(arg) # (None) -> raises an exception if arg is None: raise TypeError("Cannot parse argument of type None.") - # try (int, float) -> from timestamp with tz + # try (int, float) -> from timestamp @ tzinfo elif not isinstance(arg, str) and is_timestamp(arg): if tz is None: # set to UTC by default tz = dateutil_tz.tzutc() return self.type.fromtimestamp(arg, tzinfo=tz) - # (Arrow) -> from the object's datetime. + # (Arrow) -> from the object's datetime @ tzinfo elif isinstance(arg, Arrow): - return self.type.fromdatetime(arg.datetime) + return self.type.fromdatetime(arg.datetime, tzinfo=tz) - # (datetime) -> from datetime. + # (datetime) -> from datetime @ tzinfo elif isinstance(arg, datetime): - return self.type.fromdatetime(arg) + return self.type.fromdatetime(arg, tzinfo=tz) - # (date) -> from date. + # (date) -> from date @ tzinfo elif isinstance(arg, date): - return self.type.fromdate(arg) + return self.type.fromdate(arg, tzinfo=tz) - # (tzinfo) -> now, @ tzinfo. + # (tzinfo) -> now @ tzinfo elif isinstance(arg, dt_tzinfo): - return self.type.now(arg) + return self.type.now(tzinfo=arg) - # (str) -> parse. + # (str) -> parse @ tzinfo elif isinstance(arg, str): dt = parser.DateTimeParser(locale).parse_iso(arg, normalize_whitespace) - return self.type.fromdatetime(dt, tz) + return self.type.fromdatetime(dt, tzinfo=tz) # (struct_time) -> from struct_time elif isinstance(arg, struct_time): return self.type.utcfromtimestamp(calendar.timegm(arg)) - # (iso calendar) -> convert then from date + # (iso calendar) -> convert then from date @ tzinfo elif isinstance(arg, tuple) and len(arg) == 3: d = iso_to_gregorian(*arg) - return self.type.fromdate(d) + return self.type.fromdate(d, tzinfo=tz) else: raise TypeError(f"Cannot parse single argument of type {type(arg)!r}.") @@ -273,9 +272,9 @@ def get(self, *args: Any, **kwargs: Any) -> Arrow: if isinstance(arg_1, datetime): - # (datetime, tzinfo/str) -> fromdatetime replace tzinfo. + # (datetime, tzinfo/str) -> fromdatetime @ tzinfo if isinstance(arg_2, (dt_tzinfo, str)): - return self.type.fromdatetime(arg_1, arg_2) + return self.type.fromdatetime(arg_1, tzinfo=arg_2) else: raise TypeError( f"Cannot parse two arguments of types 'datetime', {type(arg_2)!r}." @@ -283,7 +282,7 @@ def get(self, *args: Any, **kwargs: Any) -> Arrow: elif isinstance(arg_1, date): - # (date, tzinfo/str) -> fromdate replace tzinfo. + # (date, tzinfo/str) -> fromdate @ tzinfo if isinstance(arg_2, (dt_tzinfo, str)): return self.type.fromdate(arg_1, tzinfo=arg_2) else: @@ -291,7 +290,7 @@ def get(self, *args: Any, **kwargs: Any) -> Arrow: f"Cannot parse two arguments of types 'date', {type(arg_2)!r}." ) - # (str, format) -> parse. + # (str, format) -> parse @ tzinfo elif isinstance(arg_1, str) and isinstance(arg_2, (str, list)): dt = parser.DateTimeParser(locale).parse( args[0], args[1], normalize_whitespace @@ -303,7 +302,7 @@ def get(self, *args: Any, **kwargs: Any) -> Arrow: f"Cannot parse two arguments of types {type(arg_1)!r} and {type(arg_2)!r}." ) - # 3+ args -> datetime-like via constructor. + # 3+ args -> datetime-like via constructor else: return self.type(*args, **kwargs) diff --git a/script.module.arrow/lib/arrow/formatter.py b/script.module.arrow/lib/arrow/formatter.py index 14eb44a20a..728bea1aaf 100644 --- a/script.module.arrow/lib/arrow/formatter.py +++ b/script.module.arrow/lib/arrow/formatter.py @@ -1,3 +1,5 @@ +"""Provides the :class:`Arrow ` class, an improved formatter for datetimes.""" + import re import sys from datetime import datetime, timedelta @@ -6,6 +8,7 @@ from dateutil import tz as dateutil_tz from arrow import locales +from arrow.constants import DEFAULT_LOCALE if sys.version_info < (3, 8): # pragma: no cover from typing_extensions import Final @@ -37,7 +40,7 @@ class DateTimeFormatter: locale: locales.Locale - def __init__(self, locale: str = "en_us") -> None: + def __init__(self, locale: str = DEFAULT_LOCALE) -> None: self.locale = locales.get_locale(locale) diff --git a/script.module.arrow/lib/arrow/locales.py b/script.module.arrow/lib/arrow/locales.py index d801f9d639..3627497f57 100644 --- a/script.module.arrow/lib/arrow/locales.py +++ b/script.module.arrow/lib/arrow/locales.py @@ -1,4 +1,5 @@ -import inspect +"""Provides internationalization for arrow in over 60 languages and dialects.""" + import sys from math import trunc from typing import ( @@ -34,19 +35,18 @@ "weeks", "month", "months", + "quarter", + "quarters", "year", "years", - "2-hours", - "2-days", - "2-weeks", - "2-months", - "2-years", ] _TimeFrameElements = Union[ str, Sequence[str], Mapping[str, str], Mapping[str, Sequence[str]] ] +_locale_map: Dict[str, Type["Locale"]] = {} + def get_locale(name: str) -> "Locale": """Returns an appropriate :class:`Locale ` @@ -56,10 +56,11 @@ def get_locale(name: str) -> "Locale": """ - locale_cls = _locales.get(name.lower()) + normalized_locale_name = name.lower().replace("_", "-") + locale_cls = _locale_map.get(normalized_locale_name) if locale_cls is None: - raise ValueError(f"Unsupported locale {name!r}.") + raise ValueError(f"Unsupported locale {normalized_locale_name!r}.") return locale_cls() @@ -79,11 +80,8 @@ def get_locale_by_class_name(name: str) -> "Locale": return locale_cls() -# base locale type. - - class Locale: - """ Represents locale-specific data and functionality. """ + """Represents locale-specific data and functionality.""" names: ClassVar[List[str]] = [] @@ -101,6 +99,8 @@ class Locale: "weeks": "", "month": "", "months": "", + "quarter": "", + "quarters": "", "year": "", "years": "", } @@ -121,6 +121,13 @@ class Locale: _month_name_to_ordinal: Optional[Dict[str, int]] + def __init_subclass__(cls, **kwargs: Any) -> None: + for locale_name in cls.names: + if locale_name in _locale_map: + raise LookupError(f"Duplicated locale name: {locale_name}") + + _locale_map[locale_name.lower().replace("_", "-")] = cls + def __init__(self) -> None: self._month_name_to_ordinal = None @@ -138,7 +145,7 @@ def describe( :param only_distance: return only distance eg: "11 seconds" without "in" or "ago" keywords """ - humanized = self._format_timeframe(timeframe, delta) + humanized = self._format_timeframe(timeframe, trunc(delta)) if not only_distance: humanized = self._format_relative(humanized, timeframe, delta) @@ -156,14 +163,24 @@ def describe_multi( """ parts = [ - self._format_timeframe(timeframe, delta) for timeframe, delta in timeframes + self._format_timeframe(timeframe, trunc(delta)) + for timeframe, delta in timeframes ] if self.and_word: parts.insert(-1, self.and_word) humanized = " ".join(parts) if not only_distance: - humanized = self._format_relative(humanized, *timeframes[-1]) + # Needed to determine the correct relative string to use + timeframe_value = 0 + + for _unit_name, unit_value in timeframes: + if trunc(unit_value) != 0: + timeframe_value = trunc(unit_value) + break + + # Note it doesn't matter the timeframe unit we use on the call, only the value + humanized = self._format_relative(humanized, "seconds", timeframe_value) return humanized @@ -258,9 +275,7 @@ def _ordinal_number(self, n: int) -> str: def _name_to_ordinal(self, lst: Sequence[str]) -> Dict[str, int]: return {elem.lower(): i for i, elem in enumerate(lst[1:], 1)} - def _format_timeframe( - self, timeframe: TimeFrameLiteral, delta: Union[float, int] - ) -> str: + def _format_timeframe(self, timeframe: TimeFrameLiteral, delta: int) -> str: # TODO: remove cast return cast(str, self.timeframes[timeframe]).format(trunc(abs(delta))) @@ -279,21 +294,17 @@ def _format_relative( return direction.format(humanized) -# base locale type implementations. - - class EnglishLocale(Locale): - names = [ "en", - "en_us", - "en_gb", - "en_au", - "en_be", - "en_jp", - "en_za", - "en_ca", - "en_ph", + "en-us", + "en-gb", + "en-au", + "en-be", + "en-jp", + "en-za", + "en-ca", + "en-ph", ] past = "{0} ago" @@ -314,6 +325,8 @@ class EnglishLocale(Locale): "weeks": "{0} weeks", "month": "a month", "months": "{0} months", + "quarter": "a quarter", + "quarters": "{0} quarters", "year": "a year", "years": "{0} years", } @@ -397,7 +410,7 @@ def describe( class ItalianLocale(Locale): - names = ["it", "it_it"] + names = ["it", "it-it"] past = "{0} fa" future = "tra {0}" and_word = "e" @@ -470,7 +483,7 @@ def _ordinal_number(self, n: int) -> str: class SpanishLocale(Locale): - names = ["es", "es_es"] + names = ["es", "es-es"] past = "hace {0}" future = "en {0}" and_word = "y" @@ -545,7 +558,6 @@ def _ordinal_number(self, n: int) -> str: class FrenchBaseLocale(Locale): - past = "il y a {0}" future = "dans {0}" and_word = "et" @@ -607,8 +619,7 @@ def _ordinal_number(self, n: int) -> str: class FrenchLocale(FrenchBaseLocale, Locale): - - names = ["fr", "fr_fr"] + names = ["fr", "fr-fr"] month_abbreviations = [ "", @@ -628,8 +639,7 @@ class FrenchLocale(FrenchBaseLocale, Locale): class FrenchCanadianLocale(FrenchBaseLocale, Locale): - - names = ["fr_ca"] + names = ["fr-ca"] month_abbreviations = [ "", @@ -649,8 +659,7 @@ class FrenchCanadianLocale(FrenchBaseLocale, Locale): class GreekLocale(Locale): - - names = ["el", "el_gr"] + names = ["el", "el-gr"] past = "{0} πριν" future = "σε {0}" @@ -666,6 +675,8 @@ class GreekLocale(Locale): "hours": "{0} ώρες", "day": "μία μέρα", "days": "{0} μέρες", + "week": "μία εβδομάδα", + "weeks": "{0} εβδομάδες", "month": "ένα μήνα", "months": "{0} μήνες", "year": "ένα χρόνο", @@ -717,8 +728,7 @@ class GreekLocale(Locale): class JapaneseLocale(Locale): - - names = ["ja", "ja_jp"] + names = ["ja", "ja-jp"] past = "{0}前" future = "{0}後" @@ -778,8 +788,7 @@ class JapaneseLocale(Locale): class SwedishLocale(Locale): - - names = ["sv", "sv_se"] + names = ["sv", "sv-se"] past = "för {0} sen" future = "om {0}" @@ -848,8 +857,7 @@ class SwedishLocale(Locale): class FinnishLocale(Locale): - - names = ["fi", "fi_fi"] + names = ["fi", "fi-fi"] # The finnish grammar is very complex, and its hard to convert # 1-to-1 to something like English. @@ -857,20 +865,20 @@ class FinnishLocale(Locale): past = "{0} sitten" future = "{0} kuluttua" - timeframes: ClassVar[Mapping[TimeFrameLiteral, List[str]]] = { - "now": ["juuri nyt", "juuri nyt"], - "second": ["sekunti", "sekunti"], - "seconds": ["{0} muutama sekunti", "{0} muutaman sekunnin"], - "minute": ["minuutti", "minuutin"], - "minutes": ["{0} minuuttia", "{0} minuutin"], - "hour": ["tunti", "tunnin"], - "hours": ["{0} tuntia", "{0} tunnin"], - "day": ["päivä", "päivä"], - "days": ["{0} päivää", "{0} päivän"], - "month": ["kuukausi", "kuukauden"], - "months": ["{0} kuukautta", "{0} kuukauden"], - "year": ["vuosi", "vuoden"], - "years": ["{0} vuotta", "{0} vuoden"], + timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]] = { + "now": "juuri nyt", + "second": "sekunti", + "seconds": {"past": "{0} muutama sekunti", "future": "{0} muutaman sekunnin"}, + "minute": {"past": "minuutti", "future": "minuutin"}, + "minutes": {"past": "{0} minuuttia", "future": "{0} minuutin"}, + "hour": {"past": "tunti", "future": "tunnin"}, + "hours": {"past": "{0} tuntia", "future": "{0} tunnin"}, + "day": "päivä", + "days": {"past": "{0} päivää", "future": "{0} päivän"}, + "month": {"past": "kuukausi", "future": "kuukauden"}, + "months": {"past": "{0} kuukautta", "future": "{0} kuukauden"}, + "year": {"past": "vuosi", "future": "vuoden"}, + "years": {"past": "{0} vuotta", "future": "{0} vuoden"}, } # Months and days are lowercase in Finnish @@ -919,41 +927,30 @@ class FinnishLocale(Locale): day_abbreviations = ["", "ma", "ti", "ke", "to", "pe", "la", "su"] - # TODO: Fix return type - def _format_timeframe(self, timeframe: TimeFrameLiteral, delta: Union[float, int]) -> Tuple[str, str]: # type: ignore - return ( - self.timeframes[timeframe][0].format(abs(delta)), - self.timeframes[timeframe][1].format(abs(delta)), - ) - - def _format_relative( - self, - humanized: str, - timeframe: TimeFrameLiteral, - delta: Union[float, int], - ) -> str: - if timeframe == "now": - return humanized[0] + def _format_timeframe(self, timeframe: TimeFrameLiteral, delta: int) -> str: + form = self.timeframes[timeframe] - direction = self.past if delta < 0 else self.future - which = 0 if delta < 0 else 1 + if isinstance(form, Mapping): + if delta < 0: + form = form["past"] + else: + form = form["future"] - return direction.format(humanized[which]) + return form.format(abs(delta)) def _ordinal_number(self, n: int) -> str: return f"{n}." class ChineseCNLocale(Locale): - - names = ["zh", "zh_cn"] + names = ["zh", "zh-cn"] past = "{0}前" future = "{0}后" timeframes = { "now": "刚才", - "second": "一秒", + "second": "1秒", "seconds": "{0}秒", "minute": "1分钟", "minutes": "{0}分钟", @@ -961,7 +958,7 @@ class ChineseCNLocale(Locale): "hours": "{0}小时", "day": "1天", "days": "{0}天", - "week": "一周", + "week": "1周", "weeks": "{0}周", "month": "1个月", "months": "{0}个月", @@ -1005,8 +1002,7 @@ class ChineseCNLocale(Locale): class ChineseTWLocale(Locale): - - names = ["zh_tw"] + names = ["zh-tw"] past = "{0}前" future = "{0}後" @@ -1066,8 +1062,7 @@ class ChineseTWLocale(Locale): class HongKongLocale(Locale): - - names = ["zh_hk"] + names = ["zh-hk"] past = "{0}前" future = "{0}後" @@ -1126,8 +1121,7 @@ class HongKongLocale(Locale): class KoreanLocale(Locale): - - names = ["ko", "ko_kr"] + names = ["ko", "ko-kr"] past = "{0} 전" future = "{0} 후" @@ -1222,8 +1216,7 @@ def _format_relative( # derived locale types & implementations. class DutchLocale(Locale): - - names = ["nl", "nl_nl"] + names = ["nl", "nl-nl"] past = "{0} geleden" future = "over {0}" @@ -1293,46 +1286,55 @@ class DutchLocale(Locale): class SlavicBaseLocale(Locale): - timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, List[str]]]] + timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]] - def _format_timeframe( - self, timeframe: TimeFrameLiteral, delta: Union[float, int] - ) -> str: + def _format_timeframe(self, timeframe: TimeFrameLiteral, delta: int) -> str: form = self.timeframes[timeframe] delta = abs(delta) - if isinstance(form, list): + if isinstance(form, Mapping): if delta % 10 == 1 and delta % 100 != 11: - form = form[0] + form = form["singular"] elif 2 <= delta % 10 <= 4 and (delta % 100 < 10 or delta % 100 >= 20): - form = form[1] + form = form["dual"] else: - form = form[2] + form = form["plural"] return form.format(delta) class BelarusianLocale(SlavicBaseLocale): - - names = ["be", "be_by"] + names = ["be", "be-by"] past = "{0} таму" future = "праз {0}" - timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, List[str]]]] = { + timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]] = { "now": "зараз", "second": "секунду", "seconds": "{0} некалькі секунд", "minute": "хвіліну", - "minutes": ["{0} хвіліну", "{0} хвіліны", "{0} хвілін"], + "minutes": { + "singular": "{0} хвіліну", + "dual": "{0} хвіліны", + "plural": "{0} хвілін", + }, "hour": "гадзіну", - "hours": ["{0} гадзіну", "{0} гадзіны", "{0} гадзін"], + "hours": { + "singular": "{0} гадзіну", + "dual": "{0} гадзіны", + "plural": "{0} гадзін", + }, "day": "дзень", - "days": ["{0} дзень", "{0} дні", "{0} дзён"], + "days": {"singular": "{0} дзень", "dual": "{0} дні", "plural": "{0} дзён"}, "month": "месяц", - "months": ["{0} месяц", "{0} месяцы", "{0} месяцаў"], + "months": { + "singular": "{0} месяц", + "dual": "{0} месяцы", + "plural": "{0} месяцаў", + }, "year": "год", - "years": ["{0} год", "{0} гады", "{0} гадоў"], + "years": {"singular": "{0} год", "dual": "{0} гады", "plural": "{0} гадоў"}, } month_names = [ @@ -1380,30 +1382,49 @@ class BelarusianLocale(SlavicBaseLocale): class PolishLocale(SlavicBaseLocale): - - names = ["pl", "pl_pl"] + names = ["pl", "pl-pl"] past = "{0} temu" future = "za {0}" # The nouns should be in genitive case (Polish: "dopełniacz") # in order to correctly form `past` & `future` expressions. - timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, List[str]]]] = { + timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]] = { "now": "teraz", "second": "sekundę", - "seconds": ["{0} sekund", "{0} sekundy", "{0} sekund"], + "seconds": { + "singular": "{0} sekund", + "dual": "{0} sekundy", + "plural": "{0} sekund", + }, "minute": "minutę", - "minutes": ["{0} minut", "{0} minuty", "{0} minut"], + "minutes": { + "singular": "{0} minut", + "dual": "{0} minuty", + "plural": "{0} minut", + }, "hour": "godzinę", - "hours": ["{0} godzin", "{0} godziny", "{0} godzin"], + "hours": { + "singular": "{0} godzin", + "dual": "{0} godziny", + "plural": "{0} godzin", + }, "day": "dzień", "days": "{0} dni", "week": "tydzień", - "weeks": ["{0} tygodni", "{0} tygodnie", "{0} tygodni"], + "weeks": { + "singular": "{0} tygodni", + "dual": "{0} tygodnie", + "plural": "{0} tygodni", + }, "month": "miesiąc", - "months": ["{0} miesięcy", "{0} miesiące", "{0} miesięcy"], + "months": { + "singular": "{0} miesięcy", + "dual": "{0} miesiące", + "plural": "{0} miesięcy", + }, "year": "rok", - "years": ["{0} lat", "{0} lata", "{0} lat"], + "years": {"singular": "{0} lat", "dual": "{0} lata", "plural": "{0} lat"}, } month_names = [ @@ -1451,28 +1472,49 @@ class PolishLocale(SlavicBaseLocale): class RussianLocale(SlavicBaseLocale): - - names = ["ru", "ru_ru"] + names = ["ru", "ru-ru"] past = "{0} назад" future = "через {0}" - timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, List[str]]]] = { + timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]] = { "now": "сейчас", - "second": "Второй", - "seconds": "{0} несколько секунд", + "second": "секунда", + "seconds": { + "singular": "{0} секунду", + "dual": "{0} секунды", + "plural": "{0} секунд", + }, "minute": "минуту", - "minutes": ["{0} минуту", "{0} минуты", "{0} минут"], + "minutes": { + "singular": "{0} минуту", + "dual": "{0} минуты", + "plural": "{0} минут", + }, "hour": "час", - "hours": ["{0} час", "{0} часа", "{0} часов"], + "hours": {"singular": "{0} час", "dual": "{0} часа", "plural": "{0} часов"}, "day": "день", - "days": ["{0} день", "{0} дня", "{0} дней"], + "days": {"singular": "{0} день", "dual": "{0} дня", "plural": "{0} дней"}, "week": "неделю", - "weeks": ["{0} неделю", "{0} недели", "{0} недель"], + "weeks": { + "singular": "{0} неделю", + "dual": "{0} недели", + "plural": "{0} недель", + }, "month": "месяц", - "months": ["{0} месяц", "{0} месяца", "{0} месяцев"], + "months": { + "singular": "{0} месяц", + "dual": "{0} месяца", + "plural": "{0} месяцев", + }, + "quarter": "квартал", + "quarters": { + "singular": "{0} квартал", + "dual": "{0} квартала", + "plural": "{0} кварталов", + }, "year": "год", - "years": ["{0} год", "{0} года", "{0} лет"], + "years": {"singular": "{0} год", "dual": "{0} года", "plural": "{0} лет"}, } month_names = [ @@ -1520,8 +1562,7 @@ class RussianLocale(SlavicBaseLocale): class AfrikaansLocale(Locale): - - names = ["af", "af_nl"] + names = ["af", "af-nl"] past = "{0} gelede" future = "in {0}" @@ -1587,26 +1628,37 @@ class AfrikaansLocale(Locale): class BulgarianLocale(SlavicBaseLocale): - - names = ["bg", "bg_BG"] + names = ["bg", "bg-bg"] past = "{0} назад" future = "напред {0}" - timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, List[str]]]] = { + timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]] = { "now": "сега", "second": "секунда", "seconds": "{0} няколко секунди", "minute": "минута", - "minutes": ["{0} минута", "{0} минути", "{0} минути"], + "minutes": { + "singular": "{0} минута", + "dual": "{0} минути", + "plural": "{0} минути", + }, "hour": "час", - "hours": ["{0} час", "{0} часа", "{0} часа"], + "hours": {"singular": "{0} час", "dual": "{0} часа", "plural": "{0} часа"}, "day": "ден", - "days": ["{0} ден", "{0} дни", "{0} дни"], + "days": {"singular": "{0} ден", "dual": "{0} дни", "plural": "{0} дни"}, "month": "месец", - "months": ["{0} месец", "{0} месеца", "{0} месеца"], + "months": { + "singular": "{0} месец", + "dual": "{0} месеца", + "plural": "{0} месеца", + }, "year": "година", - "years": ["{0} година", "{0} години", "{0} години"], + "years": { + "singular": "{0} година", + "dual": "{0} години", + "plural": "{0} години", + }, } month_names = [ @@ -1654,26 +1706,37 @@ class BulgarianLocale(SlavicBaseLocale): class UkrainianLocale(SlavicBaseLocale): - - names = ["ua", "uk_ua"] + names = ["ua", "uk", "uk-ua"] past = "{0} тому" future = "за {0}" - timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, List[str]]]] = { + timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]] = { "now": "зараз", "second": "секунда", "seconds": "{0} кілька секунд", "minute": "хвилину", - "minutes": ["{0} хвилину", "{0} хвилини", "{0} хвилин"], + "minutes": { + "singular": "{0} хвилину", + "dual": "{0} хвилини", + "plural": "{0} хвилин", + }, "hour": "годину", - "hours": ["{0} годину", "{0} години", "{0} годин"], + "hours": { + "singular": "{0} годину", + "dual": "{0} години", + "plural": "{0} годин", + }, "day": "день", - "days": ["{0} день", "{0} дні", "{0} днів"], + "days": {"singular": "{0} день", "dual": "{0} дні", "plural": "{0} днів"}, "month": "місяць", - "months": ["{0} місяць", "{0} місяці", "{0} місяців"], + "months": { + "singular": "{0} місяць", + "dual": "{0} місяці", + "plural": "{0} місяців", + }, "year": "рік", - "years": ["{0} рік", "{0} роки", "{0} років"], + "years": {"singular": "{0} рік", "dual": "{0} роки", "plural": "{0} років"}, } month_names = [ @@ -1721,27 +1784,47 @@ class UkrainianLocale(SlavicBaseLocale): class MacedonianLocale(SlavicBaseLocale): - names = ["mk", "mk_mk"] + names = ["mk", "mk-mk"] past = "пред {0}" future = "за {0}" - timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, List[str]]]] = { + timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]] = { "now": "сега", "second": "една секунда", - "seconds": ["{0} секунда", "{0} секунди", "{0} секунди"], + "seconds": { + "singular": "{0} секунда", + "dual": "{0} секунди", + "plural": "{0} секунди", + }, "minute": "една минута", - "minutes": ["{0} минута", "{0} минути", "{0} минути"], + "minutes": { + "singular": "{0} минута", + "dual": "{0} минути", + "plural": "{0} минути", + }, "hour": "еден саат", - "hours": ["{0} саат", "{0} саати", "{0} саати"], + "hours": {"singular": "{0} саат", "dual": "{0} саати", "plural": "{0} саати"}, "day": "еден ден", - "days": ["{0} ден", "{0} дена", "{0} дена"], + "days": {"singular": "{0} ден", "dual": "{0} дена", "plural": "{0} дена"}, "week": "една недела", - "weeks": ["{0} недела", "{0} недели", "{0} недели"], + "weeks": { + "singular": "{0} недела", + "dual": "{0} недели", + "plural": "{0} недели", + }, "month": "еден месец", - "months": ["{0} месец", "{0} месеци", "{0} месеци"], + "months": { + "singular": "{0} месец", + "dual": "{0} месеци", + "plural": "{0} месеци", + }, "year": "една година", - "years": ["{0} година", "{0} години", "{0} години"], + "years": { + "singular": "{0} година", + "dual": "{0} години", + "plural": "{0} години", + }, } meridians = {"am": "дп", "pm": "пп", "AM": "претпладне", "PM": "попладне"} @@ -1800,7 +1883,6 @@ class MacedonianLocale(SlavicBaseLocale): class GermanBaseLocale(Locale): - past = "vor {0}" future = "in {0}" and_word = "und" @@ -1906,18 +1988,15 @@ def describe( class GermanLocale(GermanBaseLocale, Locale): - - names = ["de", "de_de"] + names = ["de", "de-de"] class SwissLocale(GermanBaseLocale, Locale): - - names = ["de_ch"] + names = ["de-ch"] class AustrianLocale(GermanBaseLocale, Locale): - - names = ["de_at"] + names = ["de-at"] month_names = [ "", @@ -1937,8 +2016,7 @@ class AustrianLocale(GermanBaseLocale, Locale): class NorwegianLocale(Locale): - - names = ["nb", "nb_no"] + names = ["nb", "nb-no"] past = "for {0} siden" future = "om {0}" @@ -1953,6 +2031,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", @@ -2002,10 +2082,12 @@ 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): - - names = ["nn", "nn_no"] + names = ["nn", "nn-no"] past = "for {0} sidan" future = "om {0}" @@ -2020,7 +2102,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", @@ -2069,9 +2153,12 @@ 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"] + names = ["pt", "pt-pt"] past = "há {0}" future = "em {0}" @@ -2140,14 +2227,13 @@ class PortugueseLocale(Locale): class BrazilianPortugueseLocale(PortugueseLocale): - names = ["pt_br"] + names = ["pt-br"] past = "faz {0}" class TagalogLocale(Locale): - - names = ["tl", "tl_ph"] + names = ["tl", "tl-ph"] past = "nakaraang {0}" future = "{0} mula ngayon" @@ -2220,8 +2306,7 @@ def _ordinal_number(self, n: int) -> str: class VietnameseLocale(Locale): - - names = ["vi", "vi_vn"] + names = ["vi", "vi-vn"] past = "{0} trước" future = "{0} nữa" @@ -2289,11 +2374,11 @@ class VietnameseLocale(Locale): class TurkishLocale(Locale): - - names = ["tr", "tr_tr"] + names = ["tr", "tr-tr"] past = "{0} önce" future = "{0} sonra" + and_word = "ve" timeframes = { "now": "şimdi", @@ -2305,12 +2390,16 @@ class TurkishLocale(Locale): "hours": "{0} saat", "day": "bir gün", "days": "{0} gün", + "week": "bir hafta", + "weeks": "{0} hafta", "month": "bir ay", "months": "{0} ay", - "year": "yıl", + "year": "bir yıl", "years": "{0} yıl", } + meridians = {"am": "öö", "pm": "ös", "AM": "ÖÖ", "PM": "ÖS"} + month_names = [ "", "Ocak", @@ -2356,15 +2445,14 @@ class TurkishLocale(Locale): class AzerbaijaniLocale(Locale): - - names = ["az", "az_az"] + names = ["az", "az-az"] past = "{0} əvvəl" future = "{0} sonra" timeframes = { "now": "indi", - "second": "saniyə", + "second": "bir saniyə", "seconds": "{0} saniyə", "minute": "bir dəqiqə", "minutes": "{0} dəqiqə", @@ -2372,9 +2460,11 @@ class AzerbaijaniLocale(Locale): "hours": "{0} saat", "day": "bir gün", "days": "{0} gün", + "week": "bir həftə", + "weeks": "{0} həftə", "month": "bir ay", "months": "{0} ay", - "year": "il", + "year": "bir il", "years": "{0} il", } @@ -2425,23 +2515,23 @@ class AzerbaijaniLocale(Locale): class ArabicLocale(Locale): names = [ "ar", - "ar_ae", - "ar_bh", - "ar_dj", - "ar_eg", - "ar_eh", - "ar_er", - "ar_km", - "ar_kw", - "ar_ly", - "ar_om", - "ar_qa", - "ar_sa", - "ar_sd", - "ar_so", - "ar_ss", - "ar_td", - "ar_ye", + "ar-ae", + "ar-bh", + "ar-dj", + "ar-eg", + "ar-eh", + "ar-er", + "ar-km", + "ar-kw", + "ar-ly", + "ar-om", + "ar-qa", + "ar-sa", + "ar-sd", + "ar-so", + "ar-ss", + "ar-td", + "ar-ye", ] past = "منذ {0}" @@ -2450,17 +2540,17 @@ class ArabicLocale(Locale): timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]] = { "now": "الآن", "second": "ثانية", - "seconds": {"double": "ثانيتين", "ten": "{0} ثوان", "higher": "{0} ثانية"}, + "seconds": {"2": "ثانيتين", "ten": "{0} ثوان", "higher": "{0} ثانية"}, "minute": "دقيقة", - "minutes": {"double": "دقيقتين", "ten": "{0} دقائق", "higher": "{0} دقيقة"}, + "minutes": {"2": "دقيقتين", "ten": "{0} دقائق", "higher": "{0} دقيقة"}, "hour": "ساعة", - "hours": {"double": "ساعتين", "ten": "{0} ساعات", "higher": "{0} ساعة"}, + "hours": {"2": "ساعتين", "ten": "{0} ساعات", "higher": "{0} ساعة"}, "day": "يوم", - "days": {"double": "يومين", "ten": "{0} أيام", "higher": "{0} يوم"}, + "days": {"2": "يومين", "ten": "{0} أيام", "higher": "{0} يوم"}, "month": "شهر", - "months": {"double": "شهرين", "ten": "{0} أشهر", "higher": "{0} شهر"}, + "months": {"2": "شهرين", "ten": "{0} أشهر", "higher": "{0} شهر"}, "year": "سنة", - "years": {"double": "سنتين", "ten": "{0} سنوات", "higher": "{0} سنة"}, + "years": {"2": "سنتين", "ten": "{0} سنوات", "higher": "{0} سنة"}, } month_names = [ @@ -2506,14 +2596,12 @@ class ArabicLocale(Locale): ] day_abbreviations = ["", "إثنين", "ثلاثاء", "أربعاء", "خميس", "جمعة", "سبت", "أحد"] - def _format_timeframe( - self, timeframe: TimeFrameLiteral, delta: Union[float, int] - ) -> str: + def _format_timeframe(self, timeframe: TimeFrameLiteral, delta: int) -> str: form = self.timeframes[timeframe] delta = abs(delta) if isinstance(form, Mapping): if delta == 2: - form = form["double"] + form = form["2"] elif 2 < delta <= 10: form = form["ten"] else: @@ -2523,7 +2611,7 @@ def _format_timeframe( class LevantArabicLocale(ArabicLocale): - names = ["ar_iq", "ar_jo", "ar_lb", "ar_ps", "ar_sy"] + names = ["ar-iq", "ar-jo", "ar-lb", "ar-ps", "ar-sy"] month_names = [ "", "كانون الثاني", @@ -2557,7 +2645,7 @@ class LevantArabicLocale(ArabicLocale): class AlgeriaTunisiaArabicLocale(ArabicLocale): - names = ["ar_tn", "ar_dz"] + names = ["ar-tn", "ar-dz"] month_names = [ "", "جانفي", @@ -2591,7 +2679,7 @@ class AlgeriaTunisiaArabicLocale(ArabicLocale): class MauritaniaArabicLocale(ArabicLocale): - names = ["ar_mr"] + names = ["ar-mr"] month_names = [ "", "يناير", @@ -2625,7 +2713,7 @@ class MauritaniaArabicLocale(ArabicLocale): class MoroccoArabicLocale(ArabicLocale): - names = ["ar_ma"] + names = ["ar-ma"] month_names = [ "", "يناير", @@ -2659,37 +2747,42 @@ class MoroccoArabicLocale(ArabicLocale): class IcelandicLocale(Locale): - def _format_timeframe( - self, timeframe: TimeFrameLiteral, delta: Union[float, int] - ) -> str: + def _format_timeframe(self, timeframe: TimeFrameLiteral, delta: int) -> str: form = self.timeframes[timeframe] - if delta < 0: - form = form[0] - elif delta > 0: - form = form[1] - # FIXME: handle when delta is 0 - return form.format(abs(delta)) # type: ignore + if isinstance(form, Mapping): + if delta < 0: + form = form["past"] + elif delta > 0: + form = form["future"] + else: + raise ValueError( + "Icelandic Locale does not support units with a delta of zero. " + "Please consider making a contribution to fix this issue." + ) + # FIXME: handle when delta is 0 + + return form.format(abs(delta)) - names = ["is", "is_is"] + names = ["is", "is-is"] past = "fyrir {0} síðan" future = "eftir {0}" - timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[Tuple[str, str], str]]] = { + timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]] = { "now": "rétt í þessu", - "second": ("sekúndu", "sekúndu"), - "seconds": ("{0} nokkrum sekúndum", "nokkrar sekúndur"), - "minute": ("einni mínútu", "eina mínútu"), - "minutes": ("{0} mínútum", "{0} mínútur"), - "hour": ("einum tíma", "einn tíma"), - "hours": ("{0} tímum", "{0} tíma"), - "day": ("einum degi", "einn dag"), - "days": ("{0} dögum", "{0} daga"), - "month": ("einum mánuði", "einn mánuð"), - "months": ("{0} mánuðum", "{0} mánuði"), - "year": ("einu ári", "eitt ár"), - "years": ("{0} árum", "{0} ár"), + "second": {"past": "sekúndu", "future": "sekúndu"}, + "seconds": {"past": "{0} nokkrum sekúndum", "future": "nokkrar sekúndur"}, + "minute": {"past": "einni mínútu", "future": "eina mínútu"}, + "minutes": {"past": "{0} mínútum", "future": "{0} mínútur"}, + "hour": {"past": "einum tíma", "future": "einn tíma"}, + "hours": {"past": "{0} tímum", "future": "{0} tíma"}, + "day": {"past": "einum degi", "future": "einn dag"}, + "days": {"past": "{0} dögum", "future": "{0} daga"}, + "month": {"past": "einum mánuði", "future": "einn mánuð"}, + "months": {"past": "{0} mánuðum", "future": "{0} mánuði"}, + "year": {"past": "einu ári", "future": "eitt ár"}, + "years": {"past": "{0} árum", "future": "{0} ár"}, } meridians = {"am": "f.h.", "pm": "e.h.", "AM": "f.h.", "PM": "e.h."} @@ -2739,23 +2832,24 @@ def _format_timeframe( class DanishLocale(Locale): - - names = ["da", "da_dk"] + 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", @@ -2805,9 +2899,11 @@ 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): +class MalayalamLocale(Locale): names = ["ml"] past = "{0} മുമ്പ്" @@ -2881,8 +2977,7 @@ class MalayalamLocale(Locale): class HindiLocale(Locale): - - names = ["hi"] + names = ["hi", "hi-in"] past = "{0} पहले" future = "{0} बाद" @@ -2923,7 +3018,7 @@ class HindiLocale(Locale): month_abbreviations = [ "", "जन", - "फ़र", + "फ़र", "मार्च", "अप्रै", "मई", @@ -2950,26 +3045,59 @@ class HindiLocale(Locale): class CzechLocale(Locale): - names = ["cs", "cs_cz"] + names = ["cs", "cs-cz"] - timeframes: ClassVar[ - Mapping[TimeFrameLiteral, Union[Mapping[str, Union[List[str], str]], str]] - ] = { + timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]] = { "now": "Teď", - "second": {"past": "vteřina", "future": "vteřina", "zero": "vteřina"}, - "seconds": {"past": "{0} sekundami", "future": ["{0} sekundy", "{0} sekund"]}, - "minute": {"past": "minutou", "future": "minutu", "zero": "{0} minut"}, - "minutes": {"past": "{0} minutami", "future": ["{0} minuty", "{0} minut"]}, - "hour": {"past": "hodinou", "future": "hodinu", "zero": "{0} hodin"}, - "hours": {"past": "{0} hodinami", "future": ["{0} hodiny", "{0} hodin"]}, - "day": {"past": "dnem", "future": "den", "zero": "{0} dnů"}, - "days": {"past": "{0} dny", "future": ["{0} dny", "{0} dnů"]}, - "week": {"past": "týdnem", "future": "týden", "zero": "{0} týdnů"}, - "weeks": {"past": "{0} týdny", "future": ["{0} týdny", "{0} týdnů"]}, - "month": {"past": "měsícem", "future": "měsíc", "zero": "{0} měsíců"}, - "months": {"past": "{0} měsíci", "future": ["{0} měsíce", "{0} měsíců"]}, - "year": {"past": "rokem", "future": "rok", "zero": "{0} let"}, - "years": {"past": "{0} lety", "future": ["{0} roky", "{0} let"]}, + "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"}, + "minutes": { + "zero": "{0} minut", + "past": "{0} minutami", + "future-singular": "{0} minuty", + "future-paucal": "{0} minut", + }, + "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"}, + "days": { + "zero": "{0} dnů", + "past": "{0} dny", + "future-singular": "{0} dny", + "future-paucal": "{0} 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"}, + "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"}, + "years": { + "zero": "{0} let", + "past": "{0} lety", + "future-singular": "{0} roky", + "future-paucal": "{0} let", + }, } past = "Před {0}" @@ -3018,9 +3146,7 @@ class CzechLocale(Locale): ] day_abbreviations = ["", "po", "út", "st", "čt", "pá", "so", "ne"] - def _format_timeframe( - self, timeframe: TimeFrameLiteral, delta: Union[float, int] - ) -> str: + def _format_timeframe(self, timeframe: TimeFrameLiteral, delta: int) -> str: """Czech aware time frame format function, takes into account the differences between past and future forms.""" abs_delta = abs(delta) @@ -3031,44 +3157,77 @@ def _format_timeframe( if delta == 0: key = "zero" # And *never* use 0 in the singular! - elif delta > 0: - key = "future" - else: + elif delta < 0: key = "past" - form: Union[List[str], str] = form[key] - - if isinstance(form, list): - if 2 <= abs_delta % 10 <= 4 and ( + else: + # Needed since both regular future and future-singular and future-paucal cases + if "future-singular" not in form: + key = "future" + elif 2 <= abs_delta % 10 <= 4 and ( abs_delta % 100 < 10 or abs_delta % 100 >= 20 ): - form = form[0] + key = "future-singular" else: - form = form[1] + key = "future-paucal" + form: str = form[key] return form.format(abs_delta) class SlovakLocale(Locale): - names = ["sk", "sk_sk"] + names = ["sk", "sk-sk"] - timeframes: ClassVar[ - Mapping[TimeFrameLiteral, Union[Mapping[str, Union[List[str], str]], str]] - ] = { + timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]] = { "now": "Teraz", - "second": {"past": "sekundou", "future": "sekundu", "zero": "{0} sekúnd"}, - "seconds": {"past": "{0} sekundami", "future": ["{0} sekundy", "{0} sekúnd"]}, - "minute": {"past": "minútou", "future": "minútu", "zero": "{0} minút"}, - "minutes": {"past": "{0} minútami", "future": ["{0} minúty", "{0} minút"]}, - "hour": {"past": "hodinou", "future": "hodinu", "zero": "{0} hodín"}, - "hours": {"past": "{0} hodinami", "future": ["{0} hodiny", "{0} hodín"]}, - "day": {"past": "dňom", "future": "deň", "zero": "{0} dní"}, - "days": {"past": "{0} dňami", "future": ["{0} dni", "{0} dní"]}, - "week": {"past": "týždňom", "future": "týždeň", "zero": "{0} týždňov"}, - "weeks": {"past": "{0} týždňami", "future": ["{0} týždne", "{0} týždňov"]}, - "month": {"past": "mesiacom", "future": "mesiac", "zero": "{0} mesiacov"}, - "months": {"past": "{0} mesiacmi", "future": ["{0} mesiace", "{0} mesiacov"]}, - "year": {"past": "rokom", "future": "rok", "zero": "{0} rokov"}, - "years": {"past": "{0} rokmi", "future": ["{0} roky", "{0} rokov"]}, + "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"}, + "minutes": { + "zero": "{0} minút", + "past": "{0} minútami", + "future-singular": "{0} minúty", + "future-paucal": "{0} minút", + }, + "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ň"}, + "days": { + "zero": "{0} dní", + "past": "{0} dňami", + "future-singular": "{0} dni", + "future-paucal": "{0} dní", + }, + "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"}, + "months": { + "zero": "{0} mesiacov", + "past": "{0} mesiacmi", + "future-singular": "{0} mesiace", + "future-paucal": "{0} mesiacov", + }, + "year": {"past": "rokom", "future": "rok"}, + "years": { + "zero": "{0} rokov", + "past": "{0} rokmi", + "future-singular": "{0} roky", + "future-paucal": "{0} rokov", + }, } past = "Pred {0}" @@ -3118,9 +3277,7 @@ class SlovakLocale(Locale): ] day_abbreviations = ["", "po", "ut", "st", "št", "pi", "so", "ne"] - def _format_timeframe( - self, timeframe: TimeFrameLiteral, delta: Union[float, int] - ) -> str: + def _format_timeframe(self, timeframe: TimeFrameLiteral, delta: int) -> str: """Slovak aware time frame format function, takes into account the differences between past and future forms.""" abs_delta = abs(delta) @@ -3131,26 +3288,24 @@ def _format_timeframe( if delta == 0: key = "zero" # And *never* use 0 in the singular! - elif delta > 0: - key = "future" - else: + elif delta < 0: key = "past" - form: Union[List[str], str] = form[key] - - if isinstance(form, list): - if 2 <= abs_delta % 10 <= 4 and ( + else: + if "future-singular" not in form: + key = "future" + elif 2 <= abs_delta % 10 <= 4 and ( abs_delta % 100 < 10 or abs_delta % 100 >= 20 ): - form = form[0] + key = "future-singular" else: - form = form[1] + key = "future-paucal" + form: str = form[key] return form.format(abs_delta) class FarsiLocale(Locale): - - names = ["fa", "fa_ir"] + names = ["fa", "fa-ir"] past = "{0} قبل" future = "در {0}" @@ -3223,34 +3378,28 @@ class FarsiLocale(Locale): class HebrewLocale(Locale): - - names = ["he", "he_IL"] + names = ["he", "he-il"] past = "לפני {0}" future = "בעוד {0}" and_word = "ו" - timeframes = { + timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]] = { "now": "הרגע", "second": "שנייה", "seconds": "{0} שניות", "minute": "דקה", "minutes": "{0} דקות", "hour": "שעה", - "hours": "{0} שעות", - "2-hours": "שעתיים", + "hours": {"2": "שעתיים", "ten": "{0} שעות", "higher": "{0} שעות"}, "day": "יום", - "days": "{0} ימים", - "2-days": "יומיים", + "days": {"2": "יומיים", "ten": "{0} ימים", "higher": "{0} יום"}, "week": "שבוע", - "weeks": "{0} שבועות", - "2-weeks": "שבועיים", + "weeks": {"2": "שבועיים", "ten": "{0} שבועות", "higher": "{0} שבועות"}, "month": "חודש", - "months": "{0} חודשים", - "2-months": "חודשיים", + "months": {"2": "חודשיים", "ten": "{0} חודשים", "higher": "{0} חודשים"}, "year": "שנה", - "years": "{0} שנים", - "2-years": "שנתיים", + "years": {"2": "שנתיים", "ten": "{0} שנים", "higher": "{0} שנה"}, } meridians = { @@ -3294,20 +3443,18 @@ class HebrewLocale(Locale): day_names = ["", "שני", "שלישי", "רביעי", "חמישי", "שישי", "שבת", "ראשון"] day_abbreviations = ["", "ב׳", "ג׳", "ד׳", "ה׳", "ו׳", "ש׳", "א׳"] - def _format_timeframe( - self, timeframe: TimeFrameLiteral, delta: Union[float, int] - ) -> str: - """Hebrew couple of aware""" - couple = f"2-{timeframe}" - single = timeframe.rstrip("s") - if abs(delta) == 2 and couple in self.timeframes: - key = couple - elif abs(delta) == 1 and single in self.timeframes: - key = single - else: - key = timeframe + def _format_timeframe(self, timeframe: TimeFrameLiteral, delta: int) -> str: + form = self.timeframes[timeframe] + delta = abs(delta) + if isinstance(form, Mapping): + if delta == 2: + form = form["2"] + elif delta == 0 or 2 < delta <= 10: + form = form["ten"] + else: + form = form["higher"] - return self.timeframes[key].format(trunc(abs(delta))) + return form.format(delta) def describe_multi( self, @@ -3323,7 +3470,7 @@ def describe_multi( humanized = "" for index, (timeframe, delta) in enumerate(timeframes): - last_humanized = self._format_timeframe(timeframe, delta) + last_humanized = self._format_timeframe(timeframe, trunc(delta)) if index == 0: humanized = last_humanized elif index == len(timeframes) - 1: # Must have at least 2 items @@ -3335,13 +3482,12 @@ def describe_multi( humanized += ", " + last_humanized if not only_distance: - humanized = self._format_relative(humanized, timeframe, delta) + humanized = self._format_relative(humanized, timeframe, trunc(delta)) return humanized class MarathiLocale(Locale): - names = ["mr"] past = "{0} आधी" @@ -3409,20 +3555,8 @@ class MarathiLocale(Locale): day_abbreviations = ["", "सोम", "मंगळ", "बुध", "गुरु", "शुक्र", "शनि", "रवि"] -def _map_locales() -> Dict[str, Type[Locale]]: - - locales: Dict[str, Type[Locale]] = {} - - for _, cls in inspect.getmembers(sys.modules[__name__], inspect.isclass): - if issubclass(cls, Locale): # pragma: no branch - for name in cls.names: - locales[name.lower()] = cls - - return locales - - class CatalanLocale(Locale): - names = ["ca", "ca_es", "ca_ad", "ca_fr", "ca_it"] + names = ["ca", "ca-es", "ca-ad", "ca-fr", "ca-it"] past = "Fa {0}" future = "En {0}" and_word = "i" @@ -3431,7 +3565,7 @@ class CatalanLocale(Locale): "now": "Ara mateix", "second": "un segon", "seconds": "{0} segons", - "minute": "1 minut", + "minute": "un minut", "minutes": "{0} minuts", "hour": "una hora", "hours": "{0} hores", @@ -3496,7 +3630,7 @@ class CatalanLocale(Locale): class BasqueLocale(Locale): - names = ["eu", "eu_eu"] + names = ["eu", "eu-eu"] past = "duela {0}" future = "{0}" # I don't know what's the right phrase in Basque for the future. @@ -3560,8 +3694,7 @@ class BasqueLocale(Locale): class HungarianLocale(Locale): - - names = ["hu", "hu_hu"] + names = ["hu", "hu-hu"] past = "{0} ezelőtt" future = "{0} múlva" @@ -3627,9 +3760,7 @@ class HungarianLocale(Locale): meridians = {"am": "de", "pm": "du", "AM": "DE", "PM": "DU"} - def _format_timeframe( - self, timeframe: TimeFrameLiteral, delta: Union[float, int] - ) -> str: + def _format_timeframe(self, timeframe: TimeFrameLiteral, delta: int) -> str: form = self.timeframes[timeframe] if isinstance(form, Mapping): @@ -3642,7 +3773,7 @@ def _format_timeframe( class EsperantoLocale(Locale): - names = ["eo", "eo_xx"] + names = ["eo", "eo-xx"] past = "antaŭ {0}" future = "post {0}" @@ -3714,11 +3845,10 @@ def _ordinal_number(self, n: int) -> str: class ThaiLocale(Locale): + names = ["th", "th-th"] - names = ["th", "th_th"] - - past = "{0}{1}ที่ผ่านมา" - future = "ในอีก{1}{0}" + past = "{0} ที่ผ่านมา" + future = "ในอีก {0}" timeframes = { "now": "ขณะนี้", @@ -3793,15 +3923,126 @@ def _format_relative( """Thai normally doesn't have any space between words""" if timeframe == "now": return humanized - space = "" if timeframe == "seconds" else " " + direction = self.past if delta < 0 else self.future + relative_string = direction.format(humanized) - return direction.format(humanized, space) + if timeframe == "seconds": + relative_string = relative_string.replace(" ", "") + 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"] +class BengaliLocale(Locale): + names = ["bn", "bn-bd", "bn-in"] past = "{0} আগে" future = "{0} পরে" @@ -3827,7 +4068,7 @@ class BengaliLocale(Locale): month_names = [ "", "জানুয়ারি", - "ফেব্রুয়ারি", + "ফেব্রুয়ারি", "মার্চ", "এপ্রিল", "মে", @@ -3873,7 +4114,7 @@ def _ordinal_number(self, n: int) -> str: if n in [1, 5, 7, 8, 9, 10]: return f"{n}ম" if n in [2, 3]: - return f"{n}য়" + return f"{n}য়" if n == 4: return f"{n}র্থ" if n == 6: @@ -3881,8 +4122,7 @@ def _ordinal_number(self, n: int) -> str: class RomanshLocale(Locale): - - names = ["rm", "rm_ch"] + names = ["rm", "rm-ch"] past = "avant {0}" future = "en {0}" @@ -3950,7 +4190,7 @@ class RomanshLocale(Locale): class RomanianLocale(Locale): - names = ["ro", "ro_ro"] + names = ["ro", "ro-ro"] past = "{0} în urmă" future = "peste {0}" @@ -4017,7 +4257,7 @@ class RomanianLocale(Locale): class SlovenianLocale(Locale): - names = ["sl", "sl_si"] + names = ["sl", "sl-si"] past = "pred {0}" future = "čez {0}" @@ -4088,8 +4328,7 @@ class SlovenianLocale(Locale): class IndonesianLocale(Locale): - - names = ["id", "id_id"] + names = ["id", "id-id"] past = "{0} yang lalu" future = "dalam {0}" @@ -4105,8 +4344,12 @@ class IndonesianLocale(Locale): "hours": "{0} jam", "day": "1 hari", "days": "{0} hari", + "week": "1 minggu", + "weeks": "{0} minggu", "month": "1 bulan", "months": "{0} bulan", + "quarter": "1 kuartal", + "quarters": "{0} kuartal", "year": "1 tahun", "years": "{0} tahun", } @@ -4160,7 +4403,7 @@ class IndonesianLocale(Locale): class NepaliLocale(Locale): - names = ["ne", "ne_np"] + names = ["ne", "ne-np"] past = "{0} पहिले" future = "{0} पछी" @@ -4178,7 +4421,7 @@ class NepaliLocale(Locale): "month": "एक महिना", "months": "{0} महिना", "year": "एक बर्ष", - "years": "बर्ष", + "years": "{0} बर्ष", } meridians = {"am": "पूर्वाह्न", "pm": "अपरान्ह", "AM": "पूर्वाह्न", "PM": "अपरान्ह"} @@ -4294,9 +4537,7 @@ class EstonianLocale(Locale): ] day_abbreviations = ["", "Esm", "Teis", "Kolm", "Nelj", "Re", "Lau", "Püh"] - def _format_timeframe( - self, timeframe: TimeFrameLiteral, delta: Union[float, int] - ) -> str: + def _format_timeframe(self, timeframe: TimeFrameLiteral, delta: int) -> str: form = self.timeframes[timeframe] if delta > 0: _form = form["future"] @@ -4305,12 +4546,91 @@ def _format_timeframe( return _form.format(abs(delta)) -class SwahiliLocale(Locale): +class LatvianLocale(Locale): + names = ["lv", "lv-lv"] + + past = "pirms {0}" + future = "pēc {0}" + and_word = "un" + + timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]] = { + "now": "tagad", + "second": "sekundes", + "seconds": "{0} sekundēm", + "minute": "minūtes", + "minutes": "{0} minūtēm", + "hour": "stundas", + "hours": "{0} stundām", + "day": "dienas", + "days": "{0} dienām", + "week": "nedēļas", + "weeks": "{0} nedēļām", + "month": "mēneša", + "months": "{0} mēnešiem", + "year": "gada", + "years": "{0} gadiem", + } + + month_names = [ + "", + "janvāris", + "februāris", + "marts", + "aprīlis", + "maijs", + "jūnijs", + "jūlijs", + "augusts", + "septembris", + "oktobris", + "novembris", + "decembris", + ] + + month_abbreviations = [ + "", + "jan", + "feb", + "marts", + "apr", + "maijs", + "jūnijs", + "jūlijs", + "aug", + "sept", + "okt", + "nov", + "dec", + ] + + day_names = [ + "", + "pirmdiena", + "otrdiena", + "trešdiena", + "ceturtdiena", + "piektdiena", + "sestdiena", + "svētdiena", + ] + + day_abbreviations = [ + "", + "pi", + "ot", + "tr", + "ce", + "pi", + "se", + "sv", + ] + +class SwahiliLocale(Locale): names = [ "sw", - "sw_ke", - "sw_tz", + "sw-ke", + "sw-tz", ] past = "{0} iliyopita" @@ -4390,4 +4710,1766 @@ class SwahiliLocale(Locale): ] -_locales: Dict[str, Type[Locale]] = _map_locales() +class CroatianLocale(Locale): + names = ["hr", "hr-hr"] + + past = "prije {0}" + future = "za {0}" + and_word = "i" + + timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]] = { + "now": "upravo sad", + "second": "sekundu", + "seconds": {"double": "{0} sekunde", "higher": "{0} sekundi"}, + "minute": "minutu", + "minutes": {"double": "{0} minute", "higher": "{0} minuta"}, + "hour": "sat", + "hours": {"double": "{0} sata", "higher": "{0} sati"}, + "day": "jedan dan", + "days": {"double": "{0} dana", "higher": "{0} dana"}, + "week": "tjedan", + "weeks": {"double": "{0} tjedna", "higher": "{0} tjedana"}, + "month": "mjesec", + "months": {"double": "{0} mjeseca", "higher": "{0} mjeseci"}, + "year": "godinu", + "years": {"double": "{0} godine", "higher": "{0} godina"}, + } + + month_names = [ + "", + "siječanj", + "veljača", + "ožujak", + "travanj", + "svibanj", + "lipanj", + "srpanj", + "kolovoz", + "rujan", + "listopad", + "studeni", + "prosinac", + ] + + month_abbreviations = [ + "", + "siječ", + "velj", + "ožuj", + "trav", + "svib", + "lip", + "srp", + "kol", + "ruj", + "list", + "stud", + "pros", + ] + + day_names = [ + "", + "ponedjeljak", + "utorak", + "srijeda", + "četvrtak", + "petak", + "subota", + "nedjelja", + ] + + day_abbreviations = [ + "", + "po", + "ut", + "sr", + "če", + "pe", + "su", + "ne", + ] + + def _format_timeframe(self, timeframe: TimeFrameLiteral, delta: int) -> str: + form = self.timeframes[timeframe] + delta = abs(delta) + if isinstance(form, Mapping): + if 1 < delta <= 4: + form = form["double"] + else: + form = form["higher"] + + return form.format(delta) + + +class LatinLocale(Locale): + names = ["la", "la-va"] + + past = "ante {0}" + future = "in {0}" + and_word = "et" + + timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]] = { + "now": "nunc", + "second": "secundum", + "seconds": "{0} secundis", + "minute": "minutam", + "minutes": "{0} minutis", + "hour": "horam", + "hours": "{0} horas", + "day": "diem", + "days": "{0} dies", + "week": "hebdomadem", + "weeks": "{0} hebdomades", + "month": "mensem", + "months": "{0} mensis", + "year": "annum", + "years": "{0} annos", + } + + month_names = [ + "", + "Ianuarius", + "Februarius", + "Martius", + "Aprilis", + "Maius", + "Iunius", + "Iulius", + "Augustus", + "September", + "October", + "November", + "December", + ] + + month_abbreviations = [ + "", + "Ian", + "Febr", + "Mart", + "Apr", + "Mai", + "Iun", + "Iul", + "Aug", + "Sept", + "Oct", + "Nov", + "Dec", + ] + + day_names = [ + "", + "dies Lunae", + "dies Martis", + "dies Mercurii", + "dies Iovis", + "dies Veneris", + "dies Saturni", + "dies Solis", + ] + + day_abbreviations = [ + "", + "dies Lunae", + "dies Martis", + "dies Mercurii", + "dies Iovis", + "dies Veneris", + "dies Saturni", + "dies Solis", + ] + + +class LithuanianLocale(Locale): + names = ["lt", "lt-lt"] + + past = "prieš {0}" + future = "po {0}" + and_word = "ir" + + timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]] = { + "now": "dabar", + "second": "sekundės", + "seconds": "{0} sekundžių", + "minute": "minutės", + "minutes": "{0} minučių", + "hour": "valandos", + "hours": "{0} valandų", + "day": "dieną", + "days": "{0} dienų", + "week": "savaitės", + "weeks": "{0} savaičių", + "month": "mėnesio", + "months": "{0} mėnesių", + "year": "metų", + "years": "{0} metų", + } + + month_names = [ + "", + "sausis", + "vasaris", + "kovas", + "balandis", + "gegužė", + "birželis", + "liepa", + "rugpjūtis", + "rugsėjis", + "spalis", + "lapkritis", + "gruodis", + ] + + month_abbreviations = [ + "", + "saus", + "vas", + "kovas", + "bal", + "geg", + "birž", + "liepa", + "rugp", + "rugs", + "spalis", + "lapkr", + "gr", + ] + + day_names = [ + "", + "pirmadienis", + "antradienis", + "trečiadienis", + "ketvirtadienis", + "penktadienis", + "šeštadienis", + "sekmadienis", + ] + + day_abbreviations = [ + "", + "pi", + "an", + "tr", + "ke", + "pe", + "še", + "se", + ] + + +class MalayLocale(Locale): + names = ["ms", "ms-my", "ms-bn"] + + past = "{0} yang lalu" + future = "dalam {0}" + and_word = "dan" + + timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]] = { + "now": "sekarang", + "second": "saat", + "seconds": "{0} saat", + "minute": "minit", + "minutes": "{0} minit", + "hour": "jam", + "hours": "{0} jam", + "day": "hari", + "days": "{0} hari", + "week": "minggu", + "weeks": "{0} minggu", + "month": "bulan", + "months": "{0} bulan", + "year": "tahun", + "years": "{0} tahun", + } + + month_names = [ + "", + "Januari", + "Februari", + "Mac", + "April", + "Mei", + "Jun", + "Julai", + "Ogos", + "September", + "Oktober", + "November", + "Disember", + ] + + month_abbreviations = [ + "", + "Jan.", + "Feb.", + "Mac", + "Apr.", + "Mei", + "Jun", + "Julai", + "Og.", + "Sept.", + "Okt.", + "Nov.", + "Dis.", + ] + + day_names = [ + "", + "Isnin", + "Selasa", + "Rabu", + "Khamis", + "Jumaat", + "Sabtu", + "Ahad", + ] + + day_abbreviations = [ + "", + "Isnin", + "Selasa", + "Rabu", + "Khamis", + "Jumaat", + "Sabtu", + "Ahad", + ] + + +class MalteseLocale(Locale): + names = ["mt", "mt-mt"] + + past = "{0} ilu" + future = "fi {0}" + and_word = "u" + + timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]] = { + "now": "issa", + "second": "sekonda", + "seconds": "{0} sekondi", + "minute": "minuta", + "minutes": "{0} minuti", + "hour": "siegħa", + "hours": {"dual": "{0} sagħtejn", "plural": "{0} sigħat"}, + "day": "jum", + "days": {"dual": "{0} jumejn", "plural": "{0} ijiem"}, + "week": "ġimgħa", + "weeks": {"dual": "{0} ġimagħtejn", "plural": "{0} ġimgħat"}, + "month": "xahar", + "months": {"dual": "{0} xahrejn", "plural": "{0} xhur"}, + "year": "sena", + "years": {"dual": "{0} sentejn", "plural": "{0} snin"}, + } + + month_names = [ + "", + "Jannar", + "Frar", + "Marzu", + "April", + "Mejju", + "Ġunju", + "Lulju", + "Awwissu", + "Settembru", + "Ottubru", + "Novembru", + "Diċembru", + ] + + month_abbreviations = [ + "", + "Jan", + "Fr", + "Mar", + "Apr", + "Mejju", + "Ġun", + "Lul", + "Aw", + "Sett", + "Ott", + "Nov", + "Diċ", + ] + + day_names = [ + "", + "It-Tnejn", + "It-Tlieta", + "L-Erbgħa", + "Il-Ħamis", + "Il-Ġimgħa", + "Is-Sibt", + "Il-Ħadd", + ] + + day_abbreviations = [ + "", + "T", + "TL", + "E", + "Ħ", + "Ġ", + "S", + "Ħ", + ] + + def _format_timeframe(self, timeframe: TimeFrameLiteral, delta: int) -> str: + form = self.timeframes[timeframe] + delta = abs(delta) + if isinstance(form, Mapping): + if delta == 2: + form = form["dual"] + else: + form = form["plural"] + + return form.format(delta) + + +class SamiLocale(Locale): + names = ["se", "se-fi", "se-no", "se-se"] + + past = "{0} dassái" + future = "{0} " # NOTE: couldn't find preposition for Sami here, none needed? + + timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]] = { + "now": "dál", + "second": "sekunda", + "seconds": "{0} sekundda", + "minute": "minuhta", + "minutes": "{0} minuhta", + "hour": "diimmu", + "hours": "{0} diimmu", + "day": "beaivvi", + "days": "{0} beaivvi", + "week": "vahku", + "weeks": "{0} vahku", + "month": "mánu", + "months": "{0} mánu", + "year": "jagi", + "years": "{0} jagi", + } + + month_names = [ + "", + "Ođđajagimánnu", + "Guovvamánnu", + "Njukčamánnu", + "Cuoŋománnu", + "Miessemánnu", + "Geassemánnu", + "Suoidnemánnu", + "Borgemánnu", + "Čakčamánnu", + "Golggotmánnu", + "Skábmamánnu", + "Juovlamánnu", + ] + + month_abbreviations = [ + "", + "Ođđajagimánnu", + "Guovvamánnu", + "Njukčamánnu", + "Cuoŋománnu", + "Miessemánnu", + "Geassemánnu", + "Suoidnemánnu", + "Borgemánnu", + "Čakčamánnu", + "Golggotmánnu", + "Skábmamánnu", + "Juovlamánnu", + ] + + day_names = [ + "", + "Mánnodat", + "Disdat", + "Gaskavahkku", + "Duorastat", + "Bearjadat", + "Lávvordat", + "Sotnabeaivi", + ] + + day_abbreviations = [ + "", + "Mánnodat", + "Disdat", + "Gaskavahkku", + "Duorastat", + "Bearjadat", + "Lávvordat", + "Sotnabeaivi", + ] + + +class OdiaLocale(Locale): + names = ["or", "or-in"] + + past = "{0} ପୂର୍ବେ" + future = "{0} ପରେ" + + timeframes = { + "now": "ବର୍ତ୍ତମାନ", + "second": "ଏକ ସେକେଣ୍ଡ", + "seconds": "{0} ସେକେଣ୍ଡ", + "minute": "ଏକ ମିନଟ", + "minutes": "{0} ମିନଟ", + "hour": "ଏକ ଘଣ୍ଟା", + "hours": "{0} ଘଣ୍ଟା", + "day": "ଏକ ଦିନ", + "days": "{0} ଦିନ", + "month": "ଏକ ମାସ", + "months": "{0} ମାସ ", + "year": "ଏକ ବର୍ଷ", + "years": "{0} ବର୍ଷ", + } + + meridians = {"am": "ପୂର୍ବାହ୍ନ", "pm": "ଅପରାହ୍ନ", "AM": "ପୂର୍ବାହ୍ନ", "PM": "ଅପରାହ୍ନ"} + + month_names = [ + "", + "ଜାନୁଆରୀ", + "ଫେବୃଆରୀ", + "ମାର୍ଚ୍ଚ୍", + "ଅପ୍ରେଲ", + "ମଇ", + "ଜୁନ୍", + "ଜୁଲାଇ", + "ଅଗଷ୍ଟ", + "ସେପ୍ଟେମ୍ବର", + "ଅକ୍ଟୋବର୍", + "ନଭେମ୍ବର୍", + "ଡିସେମ୍ବର୍", + ] + month_abbreviations = [ + "", + "ଜାନୁ", + "ଫେବୃ", + "ମାର୍ଚ୍ଚ୍", + "ଅପ୍ରେ", + "ମଇ", + "ଜୁନ୍", + "ଜୁଲା", + "ଅଗ", + "ସେପ୍ଟେ", + "ଅକ୍ଟୋ", + "ନଭେ", + "ଡିସେ", + ] + + day_names = [ + "", + "ସୋମବାର", + "ମଙ୍ଗଳବାର", + "ବୁଧବାର", + "ଗୁରୁବାର", + "ଶୁକ୍ରବାର", + "ଶନିବାର", + "ରବିବାର", + ] + day_abbreviations = [ + "", + "ସୋମ", + "ମଙ୍ଗଳ", + "ବୁଧ", + "ଗୁରୁ", + "ଶୁକ୍ର", + "ଶନି", + "ରବି", + ] + + def _ordinal_number(self, n: int) -> str: + if n > 10 or n == 0: + return f"{n}ତମ" + if n in [1, 5, 7, 8, 9, 10]: + return f"{n}ମ" + if n in [2, 3]: + return f"{n}ୟ" + if n == 4: + return f"{n}ର୍ଥ" + if n == 6: + return f"{n}ଷ୍ଠ" + return "" + + +class SerbianLocale(Locale): + names = ["sr", "sr-rs", "sr-sp"] + + past = "pre {0}" + future = "za {0}" + and_word = "i" + + timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]] = { + "now": "sada", + "second": "sekundu", + "seconds": {"double": "{0} sekunde", "higher": "{0} sekundi"}, + "minute": "minutu", + "minutes": {"double": "{0} minute", "higher": "{0} minuta"}, + "hour": "sat", + "hours": {"double": "{0} sata", "higher": "{0} sati"}, + "day": "dan", + "days": {"double": "{0} dana", "higher": "{0} dana"}, + "week": "nedelju", + "weeks": {"double": "{0} nedelje", "higher": "{0} nedelja"}, + "month": "mesec", + "months": {"double": "{0} meseca", "higher": "{0} meseci"}, + "year": "godinu", + "years": {"double": "{0} godine", "higher": "{0} godina"}, + } + + month_names = [ + "", + "januar", # јануар + "februar", # фебруар + "mart", # март + "april", # април + "maj", # мај + "jun", # јун + "jul", # јул + "avgust", # август + "septembar", # септембар + "oktobar", # октобар + "novembar", # новембар + "decembar", # децембар + ] + + month_abbreviations = [ + "", + "jan", + "feb", + "mar", + "apr", + "maj", + "jun", + "jul", + "avg", + "sep", + "okt", + "nov", + "dec", + ] + + day_names = [ + "", + "ponedeljak", # понедељак + "utorak", # уторак + "sreda", # среда + "četvrtak", # четвртак + "petak", # петак + "subota", # субота + "nedelja", # недеља + ] + + day_abbreviations = [ + "", + "po", # по + "ut", # ут + "sr", # ср + "če", # че + "pe", # пе + "su", # су + "ne", # не + ] + + def _format_timeframe(self, timeframe: TimeFrameLiteral, delta: int) -> str: + form = self.timeframes[timeframe] + delta = abs(delta) + if isinstance(form, Mapping): + if 1 < delta <= 4: + form = form["double"] + else: + form = form["higher"] + + return form.format(delta) + + +class LuxembourgishLocale(Locale): + names = ["lb", "lb-lu"] + + past = "virun {0}" + future = "an {0}" + and_word = "an" + + timeframes = { + "now": "just elo", + "second": "enger Sekonn", + "seconds": "{0} Sekonnen", + "minute": "enger Minutt", + "minutes": "{0} Minutten", + "hour": "enger Stonn", + "hours": "{0} Stonnen", + "day": "engem Dag", + "days": "{0} Deeg", + "week": "enger Woch", + "weeks": "{0} Wochen", + "month": "engem Mount", + "months": "{0} Méint", + "year": "engem Joer", + "years": "{0} Jahren", + } + + timeframes_only_distance = timeframes.copy() + timeframes_only_distance["second"] = "eng Sekonn" + timeframes_only_distance["minute"] = "eng Minutt" + timeframes_only_distance["hour"] = "eng Stonn" + timeframes_only_distance["day"] = "een Dag" + timeframes_only_distance["days"] = "{0} Deeg" + timeframes_only_distance["week"] = "eng Woch" + timeframes_only_distance["month"] = "ee Mount" + timeframes_only_distance["months"] = "{0} Méint" + timeframes_only_distance["year"] = "ee Joer" + timeframes_only_distance["years"] = "{0} Joer" + + month_names = [ + "", + "Januar", + "Februar", + "Mäerz", + "Abrëll", + "Mee", + "Juni", + "Juli", + "August", + "September", + "Oktouber", + "November", + "Dezember", + ] + + month_abbreviations = [ + "", + "Jan", + "Feb", + "Mäe", + "Abr", + "Mee", + "Jun", + "Jul", + "Aug", + "Sep", + "Okt", + "Nov", + "Dez", + ] + + day_names = [ + "", + "Méindeg", + "Dënschdeg", + "Mëttwoch", + "Donneschdeg", + "Freideg", + "Samschdeg", + "Sonndeg", + ] + + day_abbreviations = ["", "Méi", "Dën", "Mët", "Don", "Fre", "Sam", "Son"] + + def _ordinal_number(self, n: int) -> str: + return f"{n}." + + def describe( + self, + timeframe: TimeFrameLiteral, + delta: Union[int, float] = 0, + only_distance: bool = False, + ) -> str: + if not only_distance: + return super().describe(timeframe, delta, only_distance) + + # Luxembourgish uses a different case without 'in' or 'ago' + humanized = self.timeframes_only_distance[timeframe].format(trunc(abs(delta))) + + return humanized + + +class ZuluLocale(Locale): + names = ["zu", "zu-za"] + + past = "{0} edlule" + future = "{0} " + and_word = "futhi" + + timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[Mapping[str, str], str]]] = { + "now": "manje", + "second": {"past": "umzuzwana", "future": "ngomzuzwana"}, + "seconds": {"past": "{0} imizuzwana", "future": "{0} ngemizuzwana"}, + "minute": {"past": "umzuzu", "future": "ngomzuzu"}, + "minutes": {"past": "{0} imizuzu", "future": "{0} ngemizuzu"}, + "hour": {"past": "ihora", "future": "ngehora"}, + "hours": {"past": "{0} amahora", "future": "{0} emahoreni"}, + "day": {"past": "usuku", "future": "ngosuku"}, + "days": {"past": "{0} izinsuku", "future": "{0} ezinsukwini"}, + "week": {"past": "isonto", "future": "ngesonto"}, + "weeks": {"past": "{0} amasonto", "future": "{0} emasontweni"}, + "month": {"past": "inyanga", "future": "ngenyanga"}, + "months": {"past": "{0} izinyanga", "future": "{0} ezinyangeni"}, + "year": {"past": "unyaka", "future": "ngonyak"}, + "years": {"past": "{0} iminyaka", "future": "{0} eminyakeni"}, + } + + def _format_timeframe(self, timeframe: TimeFrameLiteral, delta: int) -> str: + """Zulu aware time frame format function, takes into account + the differences between past and future forms.""" + abs_delta = abs(delta) + form = self.timeframes[timeframe] + + if isinstance(form, str): + return form.format(abs_delta) + + if delta > 0: + key = "future" + else: + key = "past" + form = form[key] + + return form.format(abs_delta) + + month_names = [ + "", + "uMasingane", + "uNhlolanja", + "uNdasa", + "UMbasa", + "UNhlaba", + "UNhlangulana", + "uNtulikazi", + "UNcwaba", + "uMandulo", + "uMfumfu", + "uLwezi", + "uZibandlela", + ] + + month_abbreviations = [ + "", + "uMasingane", + "uNhlolanja", + "uNdasa", + "UMbasa", + "UNhlaba", + "UNhlangulana", + "uNtulikazi", + "UNcwaba", + "uMandulo", + "uMfumfu", + "uLwezi", + "uZibandlela", + ] + + day_names = [ + "", + "uMsombuluko", + "uLwesibili", + "uLwesithathu", + "uLwesine", + "uLwesihlanu", + "uMgqibelo", + "iSonto", + ] + + day_abbreviations = [ + "", + "uMsombuluko", + "uLwesibili", + "uLwesithathu", + "uLwesine", + "uLwesihlanu", + "uMgqibelo", + "iSonto", + ] + + +class TamilLocale(Locale): + names = ["ta", "ta-in", "ta-lk"] + + past = "{0} நேரத்திற்கு முன்பு" + future = "இல் {0}" + + timeframes = { + "now": "இப்போது", + "second": "ஒரு இரண்டாவது", + "seconds": "{0} விநாடிகள்", + "minute": "ஒரு நிமிடம்", + "minutes": "{0} நிமிடங்கள்", + "hour": "ஒரு மணி", + "hours": "{0} மணிநேரம்", + "day": "ஒரு நாள்", + "days": "{0} நாட்கள்", + "week": "ஒரு வாரம்", + "weeks": "{0} வாரங்கள்", + "month": "ஒரு மாதம்", + "months": "{0} மாதங்கள்", + "year": "ஒரு ஆண்டு", + "years": "{0} ஆண்டுகள்", + } + + month_names = [ + "", + "சித்திரை", + "வைகாசி", + "ஆனி", + "ஆடி", + "ஆவணி", + "புரட்டாசி", + "ஐப்பசி", + "கார்த்திகை", + "மார்கழி", + "தை", + "மாசி", + "பங்குனி", + ] + + month_abbreviations = [ + "", + "ஜன", + "பிப்", + "மார்", + "ஏப்", + "மே", + "ஜூன்", + "ஜூலை", + "ஆக", + "செப்", + "அக்", + "நவ", + "டிச", + ] + + day_names = [ + "", + "திங்கட்கிழமை", + "செவ்வாய்க்கிழமை", + "புதன்கிழமை", + "வியாழக்கிழமை", + "வெள்ளிக்கிழமை", + "சனிக்கிழமை", + "ஞாயிற்றுக்கிழமை", + ] + + day_abbreviations = [ + "", + "திங்கட்", + "செவ்வாய்", + "புதன்", + "வியாழன்", + "வெள்ளி", + "சனி", + "ஞாயிறு", + ] + + def _ordinal_number(self, n: int) -> str: + if n == 1: + return f"{n}வது" + elif n >= 0: + return f"{n}ஆம்" + else: + return "" + + +class AlbanianLocale(Locale): + names = ["sq", "sq-al"] + + past = "{0} më parë" + future = "në {0}" + and_word = "dhe" + + timeframes = { + "now": "tani", + "second": "sekondë", + "seconds": "{0} sekonda", + "minute": "minutë", + "minutes": "{0} minuta", + "hour": "orë", + "hours": "{0} orë", + "day": "ditë", + "days": "{0} ditë", + "week": "javë", + "weeks": "{0} javë", + "month": "muaj", + "months": "{0} muaj", + "year": "vit", + "years": "{0} vjet", + } + + month_names = [ + "", + "janar", + "shkurt", + "mars", + "prill", + "maj", + "qershor", + "korrik", + "gusht", + "shtator", + "tetor", + "nëntor", + "dhjetor", + ] + + month_abbreviations = [ + "", + "jan", + "shk", + "mar", + "pri", + "maj", + "qer", + "korr", + "gush", + "sht", + "tet", + "nën", + "dhj", + ] + + day_names = [ + "", + "e hënë", + "e martë", + "e mërkurë", + "e enjte", + "e premte", + "e shtunë", + "e diel", + ] + + day_abbreviations = [ + "", + "hën", + "mar", + "mër", + "enj", + "pre", + "sht", + "die", + ] + + +class 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"] + + past = "{0}ට පෙර" + future = "{0}" + and_word = "සහ" + + timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[Mapping[str, str], str]]] = { + "now": "දැන්", + "second": { + "past": "තත්පරයක", + "future": "තත්පරයකින්", + }, # ක් is the article + "seconds": { + "past": "තත්පර {0} ක", + "future": "තත්පර {0} කින්", + }, + "minute": { + "past": "විනාඩියක", + "future": "විනාඩියකින්", + }, + "minutes": { + "past": "විනාඩි {0} ක", + "future": "මිනිත්තු {0} කින්", + }, + "hour": {"past": "පැයක", "future": "පැයකින්"}, + "hours": { + "past": "පැය {0} ක", + "future": "පැය {0} කින්", + }, + "day": {"past": "දිනක", "future": "දිනකට"}, + "days": { + "past": "දින {0} ක", + "future": "දින {0} කින්", + }, + "week": {"past": "සතියක", "future": "සතියකින්"}, + "weeks": { + "past": "සති {0} ක", + "future": "සති {0} කින්", + }, + "month": {"past": "මාසයක", "future": "එය මාසය තුළ"}, + "months": { + "past": "මාස {0} ක", + "future": "මාස {0} කින්", + }, + "year": {"past": "වසරක", "future": "වසරක් තුළ"}, + "years": { + "past": "අවුරුදු {0} ක", + "future": "අවුරුදු {0} තුළ", + }, + } + # Sinhala: the general format to describe timeframe is different from past and future, + # so we do not copy the original timeframes dictionary + timeframes_only_distance = {} + timeframes_only_distance["second"] = "තත්පරයක්" + timeframes_only_distance["seconds"] = "තත්පර {0}" + timeframes_only_distance["minute"] = "මිනිත්තුවක්" + timeframes_only_distance["minutes"] = "විනාඩි {0}" + timeframes_only_distance["hour"] = "පැයක්" + timeframes_only_distance["hours"] = "පැය {0}" + timeframes_only_distance["day"] = "දවසක්" + timeframes_only_distance["days"] = "දවස් {0}" + timeframes_only_distance["week"] = "සතියක්" + timeframes_only_distance["weeks"] = "සති {0}" + timeframes_only_distance["month"] = "මාසයක්" + timeframes_only_distance["months"] = "මාස {0}" + timeframes_only_distance["year"] = "අවුරුද්දක්" + timeframes_only_distance["years"] = "අවුරුදු {0}" + + def _format_timeframe(self, timeframe: TimeFrameLiteral, delta: int) -> str: + """ + Sinhala awares time frame format function, takes into account + the differences between general, past, and future forms (three different suffixes). + """ + abs_delta = abs(delta) + form = self.timeframes[timeframe] + + if isinstance(form, str): + return form.format(abs_delta) + + if delta > 0: + key = "future" + else: + key = "past" + form = form[key] + + return form.format(abs_delta) + + def describe( + self, + timeframe: TimeFrameLiteral, + delta: Union[float, int] = 1, # key is always future when only_distance=False + only_distance: bool = False, + ) -> str: + """Describes a delta within a timeframe in plain language. + + :param timeframe: a string representing a timeframe. + :param delta: a quantity representing a delta in a timeframe. + :param only_distance: return only distance eg: "11 seconds" without "in" or "ago" keywords + """ + + if not only_distance: + return super().describe(timeframe, delta, only_distance) + # Sinhala uses a different case without 'in' or 'ago' + humanized = self.timeframes_only_distance[timeframe].format(trunc(abs(delta))) + + return humanized + + month_names = [ + "", + "ජනවාරි", + "පෙබරවාරි", + "මාර්තු", + "අප්‍රේල්", + "මැයි", + "ජූනි", + "ජූලි", + "අගෝස්තු", + "සැප්තැම්බර්", + "ඔක්තෝබර්", + "නොවැම්බර්", + "දෙසැම්බර්", + ] + + month_abbreviations = [ + "", + "ජන", + "පෙබ", + "මාර්", + "අප්‍රේ", + "මැයි", + "ජුනි", + "ජූලි", + "අගෝ", + "සැප්", + "ඔක්", + "නොවැ", + "දෙසැ", + ] + + day_names = [ + "", + "සදුදා", + "අඟහරැවදා", + "බදාදා", + "බ්‍රහස්‍පතින්‍දා", + "සිකුරාදා", + "සෙනසුරාදා", + "ඉරිදා", + ] + + day_abbreviations = [ + "", + "සදුද", + "බදා", + "බදා", + "සිකු", + "සෙන", + "අ", + "ඉරිදා", + ] + + +class UrduLocale(Locale): + names = ["ur", "ur-pk"] + + past = "پہلے {0}" + future = "میں {0}" + and_word = "اور" + + timeframes = { + "now": "ابھی", + "second": "ایک سیکنڈ", + "seconds": "{0} سیکنڈ", + "minute": "ایک منٹ", + "minutes": "{0} منٹ", + "hour": "ایک گھنٹے", + "hours": "{0} گھنٹے", + "day": "ایک دن", + "days": "{0} دن", + "week": "ایک ہفتے", + "weeks": "{0} ہفتے", + "month": "ایک مہینہ", + "months": "{0} ماہ", + "year": "ایک سال", + "years": "{0} سال", + } + + month_names = [ + "", + "جنوری", + "فروری", + "مارچ", + "اپریل", + "مئی", + "جون", + "جولائی", + "اگست", + "ستمبر", + "اکتوبر", + "نومبر", + "دسمبر", + ] + + month_abbreviations = [ + "", + "جنوری", + "فروری", + "مارچ", + "اپریل", + "مئی", + "جون", + "جولائی", + "اگست", + "ستمبر", + "اکتوبر", + "نومبر", + "دسمبر", + ] + + day_names = [ + "", + "سوموار", + "منگل", + "بدھ", + "جمعرات", + "جمعہ", + "ہفتہ", + "اتوار", + ] + + day_abbreviations = [ + "", + "سوموار", + "منگل", + "بدھ", + "جمعرات", + "جمعہ", + "ہفتہ", + "اتوار", + ] + + +class KazakhLocale(Locale): + names = ["kk", "kk-kz"] + + past = "{0} бұрын" + future = "{0} кейін" + timeframes = { + "now": "қазір", + "second": "бір секунд", + "seconds": "{0} секунд", + "minute": "бір минут", + "minutes": "{0} минут", + "hour": "бір сағат", + "hours": "{0} сағат", + "day": "бір күн", + "days": "{0} күн", + "week": "бір апта", + "weeks": "{0} апта", + "month": "бір ай", + "months": "{0} ай", + "year": "бір жыл", + "years": "{0} жыл", + } + + month_names = [ + "", + "Қаңтар", + "Ақпан", + "Наурыз", + "Сәуір", + "Мамыр", + "Маусым", + "Шілде", + "Тамыз", + "Қыркүйек", + "Қазан", + "Қараша", + "Желтоқсан", + ] + month_abbreviations = [ + "", + "Қан", + "Ақп", + "Нау", + "Сәу", + "Мам", + "Мау", + "Шіл", + "Там", + "Қыр", + "Қаз", + "Қар", + "Жел", + ] + + day_names = [ + "", + "Дүйсембі", + "Сейсенбі", + "Сәрсенбі", + "Бейсенбі", + "Жұма", + "Сенбі", + "Жексенбі", + ] + day_abbreviations = ["", "Дс", "Сс", "Ср", "Бс", "Жм", "Сб", "Жс"] + + +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 + + +class ArmenianLocale(Locale): + names = ["hy", "hy-am"] + past = "{0} առաջ" + future = "{0}ից" + and_word = "Եվ" # Yev + + 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 = [ + "", + "երկ.", + "երեք.", + "չորեք.", + "հինգ.", + "ուրբ.", + "շաբ.", + "կիր.", + ] + + +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/script.module.arrow/lib/arrow/parser.py b/script.module.arrow/lib/arrow/parser.py index a6a617dbc8..e95d78b0d6 100644 --- a/script.module.arrow/lib/arrow/parser.py +++ b/script.module.arrow/lib/arrow/parser.py @@ -1,3 +1,5 @@ +"""Provides the :class:`Arrow ` class, a better way to parse datetime strings.""" + import re import sys from datetime import datetime, timedelta @@ -23,6 +25,7 @@ from dateutil import tz from arrow import locales +from arrow.constants import DEFAULT_LOCALE from arrow.util import next_weekday, normalize_timestamp if sys.version_info < (3, 8): # pragma: no cover @@ -155,7 +158,7 @@ class DateTimeParser: locale: locales.Locale _input_re_map: Dict[_FORMAT_TYPE, Pattern[str]] - def __init__(self, locale: str = "en_us", cache_size: int = 0) -> None: + 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() @@ -571,9 +574,12 @@ def _parse_token( elif token in ["a", "A"]: if value in (self.locale.meridians["am"], self.locale.meridians["AM"]): parts["am_pm"] = "am" + if "hour" in parts and not 0 <= parts["hour"] <= 12: + raise ParserMatchError( + f"Hour token value must be between 0 and 12 inclusive for token {token!r}." + ) elif value in (self.locale.meridians["pm"], self.locale.meridians["PM"]): parts["am_pm"] = "pm" - elif token == "W": parts["weekdate"] = value diff --git a/script.module.arrow/lib/arrow/py.typed b/script.module.arrow/lib/arrow/py.typed deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/script.module.arrow/lib/arrow/util.py b/script.module.arrow/lib/arrow/util.py index 8679131ee1..f3eaa21c9b 100644 --- a/script.module.arrow/lib/arrow/util.py +++ b/script.module.arrow/lib/arrow/util.py @@ -1,3 +1,5 @@ +"""Helpful functions used internally within arrow.""" + import datetime from typing import Any, Optional, cast @@ -57,7 +59,11 @@ def is_timestamp(value: Any) -> bool: def validate_ordinal(value: Any) -> None: - """Raise the corresponding exception if value is an invalid Gregorian ordinal.""" + """Raise an exception if value is an invalid Gregorian ordinal. + + :param value: the input to be checked + + """ if isinstance(value, bool) or not isinstance(value, int): raise TypeError(f"Ordinal must be an integer (got type {type(value)}).") if not (MIN_ORDINAL <= value <= MAX_ORDINAL): @@ -78,7 +84,13 @@ def normalize_timestamp(timestamp: float) -> float: # Credit to https://stackoverflow.com/a/1700069 def iso_to_gregorian(iso_year: int, iso_week: int, iso_day: int) -> datetime.date: - """Converts an ISO week date tuple into a datetime object.""" + """Converts an ISO week date into a datetime object. + + :param iso_year: the year + :param iso_week: the week number, each year has either 52 or 53 weeks + :param iso_day: the day numbered 1 through 7, beginning with Monday + + """ if not 1 <= iso_week <= 53: raise ValueError("ISO Calendar week value must be between 1-53.") diff --git a/script.module.arrow/icon.png b/script.module.arrow/resources/icon.png similarity index 100% rename from script.module.arrow/icon.png rename to script.module.arrow/resources/icon.png