[WIP] Pushed for backup.

... Do not build this as of yet ...
This commit is contained in:
j1nx 2023-06-01 15:16:04 +02:00
parent 5c7af8b058
commit c6460b9307
417 changed files with 43487 additions and 76 deletions

View File

@ -175,6 +175,7 @@ menu "Additional external python modules"
source "$BR2_EXTERNAL_OPENVOICEOS_PATH/package/python-gpsdclient/Config.in"
source "$BR2_EXTERNAL_OPENVOICEOS_PATH/package/python-gtts/Config.in"
source "$BR2_EXTERNAL_OPENVOICEOS_PATH/package/python-gtts_token/Config.in"
source "$BR2_EXTERNAL_OPENVOICEOS_PATH/package/python-h3/Config.in"
source "$BR2_EXTERNAL_OPENVOICEOS_PATH/package/python-hijri-converter/Config.in"
source "$BR2_EXTERNAL_OPENVOICEOS_PATH/package/python-holidays/Config.in"
source "$BR2_EXTERNAL_OPENVOICEOS_PATH/package/python-humanhash3/Config.in"
@ -395,8 +396,8 @@ menu "Skills"
source "$BR2_EXTERNAL_OPENVOICEOS_PATH/package/mycroft-skill-parrot/Config.in"
source "$BR2_EXTERNAL_OPENVOICEOS_PATH/package/skill-ovos-personal/Config.in"
source "$BR2_EXTERNAL_OPENVOICEOS_PATH/package/skill-ovos-weather/Config.in"
source "$BR2_EXTERNAL_OPENVOICEOS_PATH/package/skill-ovos-wikipedia/Config.in"
source "$BR2_EXTERNAL_OPENVOICEOS_PATH/package/skill-ovos-wolfie/Config.in"
source "$BR2_EXTERNAL_OPENVOICEOS_PATH/package/mycroft-skill-wikipedia-for-humans/Config.in"
source "$BR2_EXTERNAL_OPENVOICEOS_PATH/package/skill-youtube-music/Config.in"
endmenu
endmenu

View File

@ -241,6 +241,7 @@ BR2_PACKAGE_SOX=y
BR2_PACKAGE_SQUEEZELITE=y
BR2_PACKAGE_SQUEEZELITE_RESAMPLE=y
BR2_PACKAGE_VLC=y
BR2_PACKAGE_VORBIS_TOOLS=y
BR2_PACKAGE_GZIP=y
BR2_PACKAGE_LZIP=y
BR2_PACKAGE_LZOP=y
@ -321,6 +322,7 @@ BR2_PACKAGE_PYTHON3_CODECSCJK=y
BR2_PACKAGE_PYTHON3_CURSES=y
BR2_PACKAGE_PYTHON3_DECIMAL=y
BR2_PACKAGE_PYTHON3_OSSAUDIODEV=y
BR2_PACKAGE_PYTHON3_READLINE=y
BR2_PACKAGE_PYTHON3_SQLITE=y
BR2_PACKAGE_PYTHON3_XZ=y
BR2_PACKAGE_PYTHON_ALSAAUDIO=y
@ -328,7 +330,6 @@ BR2_PACKAGE_PYTHON_ARROW=y
BR2_PACKAGE_PYTHON_AUTOBAHN=y
BR2_PACKAGE_PYTHON_CHERRYPY=y
BR2_PACKAGE_PYTHON_COLORAMA=y
BR2_PACKAGE_PYTHON_CONFIGSHELL_FB=y
BR2_PACKAGE_PYTHON_DAEMONIZE=y
BR2_PACKAGE_PYTHON_DBUS_NEXT=y
BR2_PACKAGE_PYTHON_FILELOCK=y
@ -350,12 +351,15 @@ BR2_PACKAGE_PYTHON_OAUTHLIB=y
BR2_PACKAGE_PYTHON_PACKAGING=y
BR2_PACKAGE_PYTHON_PAHO_MQTT=y
BR2_PACKAGE_PYTHON_PEXPECT=y
BR2_PACKAGE_PYTHON_PILLOW=y
BR2_PACKAGE_PYTHON_PIP=y
BR2_PACKAGE_PYTHON_PLY=y
BR2_PACKAGE_PYTHON_PSUTIL=y
BR2_PACKAGE_PYTHON_PYGMENTS=y
BR2_PACKAGE_PYTHON_PYJWT=y
BR2_PACKAGE_PYTHON_PYUDEV=y
BR2_PACKAGE_PYTHON_QRCODE=y
BR2_PACKAGE_PYTHON_QRCODE_SVG=y
BR2_PACKAGE_PYTHON_QRCODE_PIL=y
BR2_PACKAGE_PYTHON_RPI_WS281X=y
BR2_PACKAGE_PYTHON_RUAMEL_YAML=y
BR2_PACKAGE_PYTHON_SDNOTIFY=y
@ -369,13 +373,13 @@ BR2_PACKAGE_PYTHON_TEXTTABLE=y
BR2_PACKAGE_PYTHON_TWISTED=y
BR2_PACKAGE_PYTHON_TWISTED_HTTP2=y
BR2_PACKAGE_PYTHON_TWISTED_TLS=y
BR2_PACKAGE_PYTHON_URWID=y
BR2_PACKAGE_PYTHON_VALIDATORS=y
BR2_PACKAGE_PYTHON_WATCHDOG=y
BR2_PACKAGE_PYTHON_WEBSOCKET_CLIENT=y
BR2_PACKAGE_PYTHON_ZC_LOCKFILE=y
BR2_PACKAGE_ALSA_LIB_PYTHON=y
BR2_PACKAGE_ALSA_PLUGINS=y
BR2_PACKAGE_LIBAO=y
BR2_PACKAGE_LIBSAMPLERATE=y
BR2_PACKAGE_OPUS_FIXED_POINT=y
BR2_PACKAGE_OPUSFILE=y
@ -492,7 +496,6 @@ BR2_PACKAGE_SYSTEMD_HIBERNATE=y
BR2_PACKAGE_SYSTEMD_LOCALED=y
BR2_PACKAGE_SYSTEMD_LOGIND=y
# BR2_PACKAGE_SYSTEMD_NETWORKD is not set
BR2_PACKAGE_SYSTEMD_OOMD=y
BR2_PACKAGE_SYSTEMD_POLKIT=y
BR2_PACKAGE_SYSTEMD_RANDOMSEED=y
BR2_PACKAGE_SYSTEMD_REPART=y
@ -550,7 +553,8 @@ BR2_PACKAGE_TENSORFLOW_LITE=y
BR2_PACKAGE_WHISPERCPP=y
BR2_PACKAGE_PYTHON_ADAPT_PARSER=y
BR2_PACKAGE_PYTHON_ASTRAL=y
BR2_PACKAGE_PYTHON_AUDIO_METADATA=y
BR2_PACKAGE_PYTHON_AUTOCOMMAND=y
BR2_PACKAGE_PYTHON_BITSTRUCT=y
BR2_PACKAGE_PYTHON_BOARD=y
BR2_PACKAGE_PYTHON_CLICK_DEFAULT_GROUP=y
BR2_PACKAGE_PYTHON_COMBO_LOCK=y
@ -560,20 +564,30 @@ BR2_PACKAGE_PYTHON_DATACLASSES=y
BR2_PACKAGE_PYTHON_DEPRECATED=y
BR2_PACKAGE_PYTHON_FEEDPARSER=y
BR2_PACKAGE_PYTHON_FLASK_FONTAWESOME=y
BR2_PACKAGE_PYTHON_FLASK_MAIL=y
BR2_PACKAGE_PYTHON_FLASK_SIMPLELOGIN=y
BR2_PACKAGE_PYTHON_FLASK_SOCKETIO=y
BR2_PACKAGE_PYTHON_GEOCODER=y
BR2_PACKAGE_PYTHON_GEOIP_GEOLITE2=y
BR2_PACKAGE_PYTHON_GOOGLE_API_PYTHON_CLIENT=y
BR2_PACKAGE_PYTHON_GOOGLE_TRANS_NEW=y
BR2_PACKAGE_PYTHON_GPSDCLIENT=y
BR2_PACKAGE_PYTHON_GTTS=y
BR2_PACKAGE_PYTHON_H3=y
BR2_PACKAGE_PYTHON_HIJRI_CONVERTER=y
BR2_PACKAGE_PYTHON_HOLIDAYS=y
BR2_PACKAGE_PYTHON_HUMANHASH3=y
BR2_PACKAGE_PYTHON_IMPORTLIB_METADATA=y
BR2_PACKAGE_PYTHON_JARACO_COLLECTIONS=y
BR2_PACKAGE_PYTHON_JARACO_CONTEXT=y
BR2_PACKAGE_PYTHON_JARACO_TEXT=y
BR2_PACKAGE_PYTHON_JSON_DATABASE=y
BR2_PACKAGE_PYTHON_KTHREAD=y
BR2_PACKAGE_PYTHON_LANGCODES=y
BR2_PACKAGE_PYTHON_LAZY=y
BR2_PACKAGE_PYTHON_LEVENSHTEIN=y
BR2_PACKAGE_PYTHON_MARKDOWN_IT_PY=y
BR2_PACKAGE_PYTHON_MDURL=y
BR2_PACKAGE_PYTHON_MEMORY_TEMPFILE=y
BR2_PACKAGE_PYTHON_MOCK_MSM=y
BR2_PACKAGE_PYTHON_MSM=y
@ -585,6 +599,8 @@ BR2_PACKAGE_PYTHON_NEON_SOLVER_PLUGIN_WOLFRAM_ALPHA=y
BR2_PACKAGE_PYTHON_NEON_SOLVERS=y
BR2_PACKAGE_PYTHON_NEON_TRANSFORMERS=y
BR2_PACKAGE_PYTHON_NEON_UTTERANCE_PLUGIN_RAKE=y
BR2_PACKAGE_PYTHON_NESTED_LOOKUP=y
BR2_PACKAGE_PYTHON_NLTK=y
BR2_PACKAGE_PYTHON_OAUTH2CLIENT=y
BR2_PACKAGE_PYTHON_OLEFILE=y
BR2_PACKAGE_PYTHON_PADACIOSO=y
@ -612,19 +628,25 @@ BR2_PACKAGE_PYTHON_RAKEKEYWORDS=y
BR2_PACKAGE_PYTHON_RAPIDFUZZ=y
BR2_PACKAGE_PYTHON_REQUESTS_FUTURES=y
BR2_PACKAGE_PYTHON_REVERSE_GEOCODER=y
BR2_PACKAGE_PYTHON_RICH=y
BR2_PACKAGE_PYTHON_RICH_CLICK=y
BR2_PACKAGE_PYTHON_SGMLLIB3K=y
BR2_PACKAGE_PYTHON_SMBUS2=y
BR2_PACKAGE_PYTHON_SMMAP=y
BR2_PACKAGE_PYTHON_SONOPY=y
BR2_PACKAGE_PYTHON_SOURCE=y
BR2_PACKAGE_PYTHON_SPEECH2TEXT=y
BR2_PACKAGE_PYTHON_SQLALCHEMY_JSON=y
BR2_PACKAGE_PYTHON_SYSV_IPC=y
BR2_PACKAGE_PYTHON_TAILHEAD=y
BR2_PACKAGE_PYTHON_TBM_UTILS=y
BR2_PACKAGE_PYTHON_TFLIT=y
BR2_PACKAGE_PYTHON_TUTUBO=y
BR2_PACKAGE_PYTHON_TZLOCAL=y
BR2_PACKAGE_PYTHON_URL_NORMALIZE=y
BR2_PACKAGE_PYTHON_VLC=y
BR2_PACKAGE_PYTHON_VOSK_API=y
BR2_PACKAGE_PYTHON_WEBCOLORS=y
BR2_PACKAGE_PYTHON_WEBRTCVAD=y
BR2_PACKAGE_PYTHON_WHEEL=y
BR2_PACKAGE_PYTHON_WIKIPEDIA_FOR_HUMANS=y
@ -633,6 +655,7 @@ BR2_PACKAGE_PYTHON_YAGMAIL=y
BR2_PACKAGE_PYTHON_YOUTUBE_DL=y
BR2_PACKAGE_PYTHON_YOUTUBE_SEARCHER=y
BR2_PACKAGE_PYTHON_YT_DLP=y
BR2_PACKAGE_PYTHON_ZIPP=y
BR2_PACKAGE_PYTHON_OVOS_AUDIO=y
BR2_PACKAGE_PYTHON_OVOS_CLASSIFIERS=y
BR2_PACKAGE_PYTHON_OVOS_CLI_CLIENT=y
@ -643,12 +666,9 @@ BR2_PACKAGE_PYTHON_OVOS_LISTENER=y
BR2_PACKAGE_PYTHON_OVOS_MESSAGEBUS=y
BR2_PACKAGE_PYTHON_OVOS_AUDIO_PLUGIN_SIMPLE=y
BR2_PACKAGE_PYTHON_OVOS_BACKEND_CLIENT=y
BR2_PACKAGE_PYTHON_OVOS_BACKEND_MANAGER=y
BR2_PACKAGE_PYTHON_OVOS_BUS_CLIENT=y
BR2_PACKAGE_PYTHON_OVOS_CONFIG_ASSISTANT=y
BR2_PACKAGE_PYTHON_OVOS_LINGUA_FRANCA=y
BR2_PACKAGE_PYTHON_OVOS_LOCAL_BACKEND=y
BR2_PACKAGE_PYTHON_OVOS_NOTIFICATIONS_SERVICE=y
BR2_PACKAGE_PYTHON_OVOS_OCP_AUDIO_PLUGIN=y
BR2_PACKAGE_PYTHON_OVOS_OCP_BANDCAMP_PLUGIN=y
BR2_PACKAGE_PYTHON_OVOS_OCP_DEEZER_PLUGIN=y
@ -659,17 +679,14 @@ BR2_PACKAGE_PYTHON_OVOS_OCP_RSS_PLUGIN=y
BR2_PACKAGE_PYTHON_OVOS_OCP_YOUTUBE_PLUGIN=y
BR2_PACKAGE_PYTHON_OVOS_PHAL=y
BR2_PACKAGE_PYTHON_OVOS_PHAL_PLUGIN_ALSA=y
BR2_PACKAGE_PYTHON_OVOS_PHAL_PLUGIN_BALENA_WIFI=y
BR2_PACKAGE_PYTHON_OVOS_PHAL_PLUGIN_CONFIGURATION_PROVIDER=y
BR2_PACKAGE_PYTHON_OVOS_PHAL_PLUGIN_CONNECTIVITY_EVENTS=y
BR2_PACKAGE_PYTHON_OVOS_PHAL_PLUGIN_DASHBOARD=y
BR2_PACKAGE_PYTHON_OVOS_PHAL_PLUGIN_HOMEASSISTANT=y
BR2_PACKAGE_PYTHON_OVOS_PHAL_PLUGIN_IPGEO=y
BR2_PACKAGE_PYTHON_OVOS_PHAL_PLUGIN_NETWORK_MANAGER=y
BR2_PACKAGE_PYTHON_OVOS_PHAL_PLUGIN_OAUTH=y
BR2_PACKAGE_PYTHON_OVOS_PHAL_PLUGIN_SYSTEM=y
BR2_PACKAGE_PYTHON_OVOS_SKILL_INSTALLER=y
BR2_PACKAGE_PYTHON_OVOS_SKILL_MANAGER=y
BR2_PACKAGE_PYTHON_OVOS_STT_HTTP_SERVER=y
BR2_PACKAGE_PYTHON_OVOS_STT_PLUGIN_CHROMIUM=y
BR2_PACKAGE_PYTHON_OVOS_STT_PLUGIN_SELENE=y
BR2_PACKAGE_PYTHON_OVOS_STT_PLUGIN_SERVER=y
@ -680,7 +697,6 @@ BR2_PACKAGE_PYTHON_OVOS_TTS_PLUGIN_MIMIC=y
BR2_PACKAGE_PYTHON_OVOS_TTS_PLUGIN_MIMIC2=y
BR2_PACKAGE_PYTHON_OVOS_TTS_PLUGIN_MIMIC3_SERVER=y
BR2_PACKAGE_PYTHON_OVOS_TTS_PLUGIN_PICO=y
BR2_PACKAGE_PYTHON_OVOS_TTS_SERVER=y
BR2_PACKAGE_PYTHON_OVOS_TTS_SERVER_PLUGIN=y
BR2_PACKAGE_PYTHON_OVOS_UTILS=y
BR2_PACKAGE_PYTHON_OVOS_VAD_PLUGIN_WEBRTCVAD=y
@ -699,10 +715,10 @@ BR2_PACKAGE_SKILL_OVOS_NAPTIME=y
BR2_PACKAGE_SKILL_OVOS_NEWS=y
BR2_PACKAGE_SKILL_OVOS_NOTES=y
BR2_PACKAGE_SKILL_OVOS_SETTINGS=y
BR2_PACKAGE_SKILL_OVOS_SETUP=y
BR2_PACKAGE_SKILL_OVOS_STOP=y
BR2_PACKAGE_SKILL_OVOS_VOLUME=y
BR2_PACKAGE_SKILL_OVOS_PERSONAL=y
BR2_PACKAGE_SKILL_OVOS_WEATHER=y
BR2_PACKAGE_SKILL_OVOS_WIKIPEDIA=y
BR2_PACKAGE_SKILL_OVOS_WOLFIE=y
BR2_PACKAGE_SKILL_YOUTUBE_MUSIC=y

View File

@ -1,5 +1,4 @@
# md5, sha256 from https://pypi.org/pypi/flask-simplelogin/json
md5 9fae54f8fd1414e52e4a6755499f047a flask_simplelogin-0.1.0.tar.gz
sha256 a1fb73b7fb37001093d76efc582b085eedc4fca8727a64082ae3f6421411c649 flask_simplelogin-0.1.0.tar.gz
# sha256 from https://pypi.org/pypi/flask-simplelogin/json
sha256 a99b7ded371aa0f5ecf3271c160e3fd52653abd5d879acf9ae621c4825345a8f flask_simplelogin-0.1.2.tar.gz
# Locally computed sha256 checksums
sha256 af616823fd3518096256d0ba51fa7078d2be93065e53854e41724ae2f1af71d8 LICENSE

View File

@ -4,9 +4,9 @@
#
################################################################################
PYTHON_FLASK_SIMPLELOGIN_VERSION = 0.1.0
PYTHON_FLASK_SIMPLELOGIN_VERSION = 0.1.2
PYTHON_FLASK_SIMPLELOGIN_SOURCE = flask_simplelogin-$(PYTHON_FLASK_SIMPLELOGIN_VERSION).tar.gz
PYTHON_FLASK_SIMPLELOGIN_SITE = https://files.pythonhosted.org/packages/34/18/660cd00c6153dddefea29ae6a8274ab2a8bbb6b124fe4d23db944a6a079c
PYTHON_FLASK_SIMPLELOGIN_SITE = https://files.pythonhosted.org/packages/e3/9b/942c42c1122e502e0bea52ac96230591df3bf58369de11ad3ad2e0c448d3
PYTHON_FLASK_SIMPLELOGIN_SETUP_TYPE = setuptools
PYTHON_FLASK_SIMPLELOGIN_LICENSE = MIT
PYTHON_FLASK_SIMPLELOGIN_LICENSE_FILES = LICENSE

View File

@ -0,0 +1,6 @@
config BR2_PACKAGE_PYTHON_H3
bool "python-h3"
help
Uber's H3 Hexagonal Hierarchical Geospatial Indexing System in Python
https://github.com/uber/h3-py

View File

@ -0,0 +1,2 @@
# sha256 from https://pypi.org/pypi/h3/json
sha256 9bbd3dbac99532fa521d7d2e288ff55877bea3223b070f659ed7b5f8f1f213eb h3-3.7.6.tar.gz

View File

@ -0,0 +1,21 @@
################################################################################
#
# python-h3
#
################################################################################
PYTHON_H3_VERSION = 3.7.6
PYTHON_H3_SOURCE = h3-$(PYTHON_H3_VERSION).tar.gz
PYTHON_H3_SITE = https://files.pythonhosted.org/packages/0e/92/30070479f7c41d66dc5f0ac44298eaedf4a7dad722348dac82d6dcc7ddd8
PYTHON_H3_SETUP_TYPE = setuptools
PYTHON_H3_LICENSE = MIT
PYTHON_H3_LICENSE_FILES = LICENSE
PYTHON_H3_DEPENDENCIES = host-python-scikit-build host-python-wheel host-python-distro
PYTHON_H3_BUILD_OPTS = -DPython3_INCLUDE_DIR:PATH=$(STAGING_DIR)/usr/include/python$(PYTHON3_VERSION_MAJOR) \
-DPYTHON_INCLUDE_DIR:PATH=$(STAGING_DIR)/usr/include/python$(PYTHON3_VERSION_MAJOR) \
-DBUILD_SHARED_LIBS=ON
PYTHON_H3_INSTALL_TARGET_OPTS = -DPython3_INCLUDE_DIR:PATH=$(STAGING_DIR)/usr/include/python$(PYTHON3_VERSION_MAJOR) \
-DPYTHON_INCLUDE_DIR:PATH=$(STAGING_DIR)/usr/include/python$(PYTHON3_VERSION_MAJOR) \
-DBUILD_SHARED_LIBS=ON
$(eval $(python-package))

View File

@ -1 +1 @@
sha256 1511291c17d5747ce78468f5e05c6e6fa4c41dd68ee5e08507047c54cce5138c python-ovos-lingua-franca-0c4ead9b3ad396fc68a5b5c6465678b567c6c01f.tar.gz
sha256 c301b8624d87cac5a6e44b6d211771347f94d4f4a408ff92386c8ba9099e3a16 python-ovos-lingua-franca-282db576c4cd0c8212eaf00ad303f0653ea0e0ed.tar.gz

View File

@ -4,7 +4,7 @@
#
################################################################################
PYTHON_OVOS_LINGUA_FRANCA_VERSION = 0c4ead9b3ad396fc68a5b5c6465678b567c6c01f
PYTHON_OVOS_LINGUA_FRANCA_VERSION = 282db576c4cd0c8212eaf00ad303f0653ea0e0ed
PYTHON_OVOS_LINGUA_FRANCA_SITE = $(call github,OpenVoiceOS,ovos-lingua-franca,$(PYTHON_OVOS_LINGUA_FRANCA_VERSION))
PYTHON_OVOS_LINGUA_FRANCA_SETUP_TYPE = setuptools
PYTHON_OVOS_LINGUA_FRANCA_LICENSE = Apache-2.0

View File

@ -0,0 +1,2 @@
# sha256 from https://pypi.org/pypi/scikit_build/json
sha256 76856e7631d9e8887a7aa71913d5f184a6177246225391af96ce4801d89fa254 scikit_build-0.17.5.tar.gz

View File

@ -0,0 +1,14 @@
################################################################################
#
# python-scikit-build
#
################################################################################
PYTHON_SCIKIT_BUILD_VERSION = 0.17.5
PYTHON_SCIKIT_BUILD_SOURCE = scikit_build-$(PYTHON_SCIKIT_BUILD_VERSION).tar.gz
PYTHON_SCIKIT_BUILD_SITE = https://files.pythonhosted.org/packages/f8/23/a07369d9095648b61a302cd45c9661ef2d92fe546e0eb28a467c66c7c1d3
PYTHON_SCIKIT_BUILD_LICENSE = MIT
PYTHON_SCIKIT_BUILD_LICENSE_FILES = LICENSE
PYTHON_SCIKIT_BUILD_SETUP_TYPE = flit
$(eval $(host-python-package))

View File

@ -1,38 +0,0 @@
From aabf33f93a95b4ccbd900dfee2b7508227ee9ac7 Mon Sep 17 00:00:00 2001
From: j1nx <p.steenbergen@j1nx.nl>
Date: Thu, 7 Jan 2021 19:21:24 +0100
Subject: [PATCH 1/1] No package name variables
---
setup.py | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/setup.py b/setup.py
index d2afc06..0cfac34 100755
--- a/setup.py
+++ b/setup.py
@@ -11,15 +11,15 @@ setup(
# version: in VERSION file https://packaging.python.org/guides/single-sourcing-package-version/
# With this approach you must make sure that the VERSION file is included in all your source
# and binary distributions (e.g. add include VERSION to your MANIFEST.in).
- author='Jannik Michelfeit',
+ author='J. Michelfeit',
author_email='python@michelfe.it',
license='MIT licence',
- url=f'https://github.com/MrMinimal64/{PACKAGE_NAME}', # use the URL to the github repo
+ url='https://github.com/MrMinimal64/timezonefinder', # use the URL to the github repo
project_urls={
- "Source Code": f"https://github.com/MrMinimal64/{PACKAGE_NAME}",
- "Documentation": f"https://{PACKAGE_NAME}.readthedocs.io/en/latest/",
- "Changelog": f"https://github.com/MrMinimal64/{PACKAGE_NAME}/blob/master/CHANGELOG.rst",
- "License": f"https://github.com/MrMinimal64/{PACKAGE_NAME}/blob/master/LICENSE",
+ "Source Code": "https://github.com/MrMinimal64/timezonefinder",
+ "Documentation": "https://timezonefinder.readthedocs.io/en/latest/",
+ "Changelog": "https://github.com/MrMinimal64/timezonefinder/blob/master/CHANGELOG.rst",
+ "License": "https://github.com/MrMinimal64/timezonefinder/blob/master/LICENSE",
},
keywords='timezone coordinates latitude longitude location pytzwhere tzwhere',
classifiers=[
--
2.20.1

View File

@ -1,2 +1,2 @@
# md5, sha256 from https://pypi.org/pypi/
sha256 d8a05c207aab92d6f01b426dc4bcef08118ec31cf4504bd4011c7551f3160694 timezonefinder-5.0.0.tar.gz
sha256 d41fd2650bb4221fae5a61f9c2767158f9727c4aaca95e24da86394feb704220 timezonefinder-6.2.0.tar.gz

View File

@ -4,10 +4,10 @@
#
################################################################################
PYTHON_TIMEZONEFINDER_VERSION = 5.0.0
PYTHON_TIMEZONEFINDER_VERSION = 6.2.0
PYTHON_TIMEZONEFINDER_SOURCE = timezonefinder-$(PYTHON_TIMEZONEFINDER_VERSION).tar.gz
PYTHON_TIMEZONEFINDER_SITE = https://files.pythonhosted.org/packages/74/d2/e6cc8edf6a6da56c5311776334b4bd2a271757573dd020592a8540512b88
PYTHON_TIMEZONEFINDER_SETUP_TYPE = setuptools
PYTHON_TIMEZONEFINDER_SITE = https://files.pythonhosted.org/packages/e7/68/86c8d0b21573150eb7b3c66da2c451159b0159c15d3d59b17777ea557f48
PYTHON_TIMEZONEFINDER_SETUP_TYPE = pep517 #flit #setuptools
PYTHON_TIMEZONEFINDER_DEPENDENCIES = host-python-numpy
$(eval $(python-package))

View File

@ -10,3 +10,4 @@ PYTHON_WHEEL_SITE = https://files.pythonhosted.org/packages/4e/be/8139f127b4db2f
PYTHON_WHEEL_SETUP_TYPE = setuptools
$(eval $(python-package))
$(eval $(host-python-package))

View File

@ -71,5 +71,8 @@
}
}
},
"log_level": "INFO"
"log_level": "INFO",
"logs": {
"path": "/var/log/mycroft"
}
}

View File

@ -4,10 +4,16 @@ vm.dirty_background_ratio = 1
vm.dirty_ratio = 50
vm.dirty_expire_centisecs = 500
vm.dirty_writeback_centisecs = 500
vm.overcommit_memory = 2
vm.overcommit_ratio = 100
kernel.panic = 20
kernel.printk = 3 4 1 3
fs.inotify.max_user_instances = 512
fs.inotify.max_user_watches = 524288
kernel.printk = 3 4 1 3
net.core.rmem_max = 4194304
net.core.wmem_max = 4194304
net.ipv4.igmp_max_memberships = 1024

View File

@ -0,0 +1,8 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
import re
import sys
from timezonefinder.command_line import main
if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
sys.exit(main())

View File

@ -0,0 +1,20 @@
Copyright (c) 2015 Sébastien Eustace
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,253 @@
Metadata-Version: 2.1
Name: pendulum
Version: 2.1.2
Summary: Python datetimes made easy
Home-page: https://pendulum.eustace.io
License: MIT
Keywords: datetime,date,time
Author: Sébastien Eustace
Author-email: sebastien@eustace.io
Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 2
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.5
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Requires-Dist: python-dateutil (>=2.6,<3.0)
Requires-Dist: pytzdata (>=2020.1)
Requires-Dist: typing (>=3.6,<4.0) ; python_version < "3.5"
Project-URL: Documentation, https://pendulum.eustace.io/docs
Project-URL: Repository, https://github.com/sdispater/pendulum
Description-Content-Type: text/x-rst
Pendulum
########
.. image:: https://img.shields.io/pypi/v/pendulum.svg
:target: https://pypi.python.org/pypi/pendulum
.. image:: https://img.shields.io/pypi/l/pendulum.svg
:target: https://pypi.python.org/pypi/pendulum
.. image:: https://img.shields.io/codecov/c/github/sdispater/pendulum/master.svg
:target: https://codecov.io/gh/sdispater/pendulum/branch/master
.. image:: https://travis-ci.org/sdispater/pendulum.svg
:alt: Pendulum Build status
:target: https://travis-ci.org/sdispater/pendulum
Python datetimes made easy.
Supports Python **2.7** and **3.4+**.
.. code-block:: python
>>> import pendulum
>>> now_in_paris = pendulum.now('Europe/Paris')
>>> now_in_paris
'2016-07-04T00:49:58.502116+02:00'
# Seamless timezone switching
>>> now_in_paris.in_timezone('UTC')
'2016-07-03T22:49:58.502116+00:00'
>>> tomorrow = pendulum.now().add(days=1)
>>> last_week = pendulum.now().subtract(weeks=1)
>>> past = pendulum.now().subtract(minutes=2)
>>> past.diff_for_humans()
>>> '2 minutes ago'
>>> delta = past - last_week
>>> delta.hours
23
>>> delta.in_words(locale='en')
'6 days 23 hours 58 minutes'
# Proper handling of datetime normalization
>>> pendulum.datetime(2013, 3, 31, 2, 30, tz='Europe/Paris')
'2013-03-31T03:30:00+02:00' # 2:30 does not exist (Skipped time)
# Proper handling of dst transitions
>>> just_before = pendulum.datetime(2013, 3, 31, 1, 59, 59, 999999, tz='Europe/Paris')
'2013-03-31T01:59:59.999999+01:00'
>>> just_before.add(microseconds=1)
'2013-03-31T03:00:00+02:00'
Why Pendulum?
=============
Native ``datetime`` instances are enough for basic cases but when you face more complex use-cases
they often show limitations and are not so intuitive to work with.
``Pendulum`` provides a cleaner and more easy to use API while still relying on the standard library.
So it's still ``datetime`` but better.
Unlike other datetime libraries for Python, Pendulum is a drop-in replacement
for the standard ``datetime`` class (it inherits from it), so, basically, you can replace all your ``datetime``
instances by ``DateTime`` instances in you code (exceptions exist for libraries that check
the type of the objects by using the ``type`` function like ``sqlite3`` or ``PyMySQL`` for instance).
It also removes the notion of naive datetimes: each ``Pendulum`` instance is timezone-aware
and by default in ``UTC`` for ease of use.
Pendulum also improves the standard ``timedelta`` class by providing more intuitive methods and properties.
Why not Arrow?
==============
Arrow is the most popular datetime library for Python right now, however its behavior
and API can be erratic and unpredictable. The ``get()`` method can receive pretty much anything
and it will try its best to return something while silently failing to handle some cases:
.. code-block:: python
arrow.get('2016-1-17')
# <Arrow [2016-01-01T00:00:00+00:00]>
pendulum.parse('2016-1-17')
# <Pendulum [2016-01-17T00:00:00+00:00]>
arrow.get('20160413')
# <Arrow [1970-08-22T08:06:53+00:00]>
pendulum.parse('20160413')
# <Pendulum [2016-04-13T00:00:00+00:00]>
arrow.get('2016-W07-5')
# <Arrow [2016-01-01T00:00:00+00:00]>
pendulum.parse('2016-W07-5')
# <Pendulum [2016-02-19T00:00:00+00:00]>
# Working with DST
just_before = arrow.Arrow(2013, 3, 31, 1, 59, 59, 999999, 'Europe/Paris')
just_after = just_before.replace(microseconds=1)
'2013-03-31T02:00:00+02:00'
# Should be 2013-03-31T03:00:00+02:00
(just_after.to('utc') - just_before.to('utc')).total_seconds()
-3599.999999
# Should be 1e-06
just_before = pendulum.datetime(2013, 3, 31, 1, 59, 59, 999999, 'Europe/Paris')
just_after = just_before.add(microseconds=1)
'2013-03-31T03:00:00+02:00'
(just_after.in_timezone('utc') - just_before.in_timezone('utc')).total_seconds()
1e-06
Those are a few examples showing that Arrow cannot always be trusted to have a consistent
behavior with the data you are passing to it.
Limitations
===========
Even though the ``DateTime`` class is a subclass of ``datetime`` there are some rare cases where
it can't replace the native class directly. Here is a list (non-exhaustive) of the reported cases with
a possible solution, if any:
* ``sqlite3`` will use the ``type()`` function to determine the type of the object by default. To work around it you can register a new adapter:
.. code-block:: python
from pendulum import DateTime
from sqlite3 import register_adapter
register_adapter(DateTime, lambda val: val.isoformat(' '))
* ``mysqlclient`` (former ``MySQLdb``) and ``PyMySQL`` will use the ``type()`` function to determine the type of the object by default. To work around it you can register a new adapter:
.. code-block:: python
import MySQLdb.converters
import pymysql.converters
from pendulum import DateTime
MySQLdb.converters.conversions[DateTime] = MySQLdb.converters.DateTime2literal
pymysql.converters.conversions[DateTime] = pymysql.converters.escape_datetime
* ``django`` will use the ``isoformat()`` method to store datetimes in the database. However since ``pendulum`` is always timezone aware the offset information will always be returned by ``isoformat()`` raising an error, at least for MySQL databases. To work around it you can either create your own ``DateTimeField`` or use the previous workaround for ``MySQLdb``:
.. code-block:: python
from django.db.models import DateTimeField as BaseDateTimeField
from pendulum import DateTime
class DateTimeField(BaseDateTimeField):
def value_to_string(self, obj):
val = self.value_from_object(obj)
if isinstance(value, DateTime):
return value.to_datetime_string()
return '' if val is None else val.isoformat()
Resources
=========
* `Official Website <https://pendulum.eustace.io>`_
* `Documentation <https://pendulum.eustace.io/docs/>`_
* `Issue Tracker <https://github.com/sdispater/pendulum/issues>`_
Contributing
============
Contributions are welcome, especially with localization.
Getting started
---------------
To work on the Pendulum codebase, you'll want to clone the project locally
and install the required depedendencies via `poetry <https://poetry.eustace.io>`_.
.. code-block:: bash
$ git clone git@github.com:sdispater/pendulum.git
$ poetry install
Localization
------------
If you want to help with localization, there are two different cases: the locale already exists
or not.
If the locale does not exist you will need to create it by using the ``clock`` utility:
.. code-block:: bash
./clock locale create <your-locale>
It will generate a directory in ``pendulum/locales`` named after your locale, with the following
structure:
.. code-block:: text
<your-locale>/
- custom.py
- locale.py
The ``locale.py`` file must not be modified. It contains the translations provided by
the CLDR database.
The ``custom.py`` file is the one you want to modify. It contains the data needed
by Pendulum that are not provided by the CLDR database. You can take the `en <https://github.com/sdispater/pendulum/tree/master/pendulum/locales/en/custom.py>`_
data as a reference to see which data is needed.
You should also add tests for the created or modified locale.

View File

@ -0,0 +1,193 @@
pendulum-2.1.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
pendulum-2.1.2.dist-info/LICENSE,sha256=Jhgfk_52I_W_PhHK7XPsx_u0meJZ9TQzARchMPboFR0,1082
pendulum-2.1.2.dist-info/METADATA,sha256=8Y18g-l0NDDZKdf0sOA38a874pY5qEAXbhBlE6_716c,8559
pendulum-2.1.2.dist-info/RECORD,,
pendulum-2.1.2.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
pendulum-2.1.2.dist-info/WHEEL,sha256=Ggj0fLZgB8T39FttQfxsvhk_5uZkqtSwLlvaw_6TAgo,110
pendulum/__init__.py,sha256=I9yZ1ahXqHSNgtnZMJH3d1c0F_DXotVGrTotbIElY10,8597
pendulum/__pycache__/__init__.cpython-311.pyc,,
pendulum/__pycache__/__version__.cpython-311.pyc,,
pendulum/__pycache__/constants.cpython-311.pyc,,
pendulum/__pycache__/date.cpython-311.pyc,,
pendulum/__pycache__/datetime.cpython-311.pyc,,
pendulum/__pycache__/duration.cpython-311.pyc,,
pendulum/__pycache__/exceptions.cpython-311.pyc,,
pendulum/__pycache__/helpers.cpython-311.pyc,,
pendulum/__pycache__/parser.cpython-311.pyc,,
pendulum/__pycache__/period.cpython-311.pyc,,
pendulum/__pycache__/time.cpython-311.pyc,,
pendulum/__version__.py,sha256=ByWE4qw8r36fv4OeLSjIm5p0j96GhZ__fEvMV0EfSWA,23
pendulum/_extensions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
pendulum/_extensions/__pycache__/__init__.cpython-311.pyc,,
pendulum/_extensions/__pycache__/helpers.cpython-311.pyc,,
pendulum/_extensions/_helpers.c,sha256=7bRT-aSD4i3JXFSYFn5l61bMGWBcvSaN9zfhjEh6rto,25967
pendulum/_extensions/helpers.py,sha256=j35ZG0wS5BMCx7JURA8PHZo4OAyNJAjriWXN-kZHc20,9628
pendulum/constants.py,sha256=dC8X7rpmZtAZdHLBavg7aJH-SaNxFpuRTnvldnIbtpQ,2921
pendulum/date.py,sha256=T95mBE0nEzLHG6fYWyUU13bnp4fne7J1NcaYxmaAFdo,25553
pendulum/datetime.py,sha256=ozcljD8A9ZHZNgqGItXXkzYy1Lv8PIzLBFLkbgUZiwA,44014
pendulum/duration.py,sha256=VpS69PUfHxGxKxdwcUmR6MJaiMEWk5TfDArfAsxebMo,13111
pendulum/exceptions.py,sha256=lj-GpJsrpl-exkmV86Wr0lobPkEo6zAIf5twV7RXBBc,106
pendulum/formatting/__init__.py,sha256=U7cXnts7Cvugr2AaECCYFAAhyV4MfMtglNvjMkRGRPY,63
pendulum/formatting/__pycache__/__init__.cpython-311.pyc,,
pendulum/formatting/__pycache__/difference_formatter.cpython-311.pyc,,
pendulum/formatting/__pycache__/formatter.cpython-311.pyc,,
pendulum/formatting/difference_formatter.py,sha256=Q9T7m1BF0WT8AHDQXajvvGAZ9sCGKyy5SE6RXN9ye38,4545
pendulum/formatting/formatter.py,sha256=-xJkF2o9LyMgbAPWrFsMSaHV6mY3WxcxaELOupoCF_0,22604
pendulum/helpers.py,sha256=mdcQhQMxICpXQOtGyVX87LFz_my8fVXVzt7Dz0cWbJM,5505
pendulum/locales/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
pendulum/locales/__pycache__/__init__.cpython-311.pyc,,
pendulum/locales/__pycache__/locale.cpython-311.pyc,,
pendulum/locales/da/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
pendulum/locales/da/__pycache__/__init__.cpython-311.pyc,,
pendulum/locales/da/__pycache__/custom.cpython-311.pyc,,
pendulum/locales/da/__pycache__/locale.cpython-311.pyc,,
pendulum/locales/da/custom.py,sha256=UBThGUwvsM3NFkkARfxagdc3TOG02ENDrWepJcdSJxQ,451
pendulum/locales/da/locale.py,sha256=U_d0jLheiL5YVyzlxpK79kQgXDGZD1JR4lgtRBZ6R0U,4961
pendulum/locales/de/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
pendulum/locales/de/__pycache__/__init__.cpython-311.pyc,,
pendulum/locales/de/__pycache__/custom.cpython-311.pyc,,
pendulum/locales/de/__pycache__/locale.cpython-311.pyc,,
pendulum/locales/de/custom.py,sha256=voSoKimREV6hw6_3iLY3QZ2iyzks8QQ8qCFKHCKT3jM,1137
pendulum/locales/de/locale.py,sha256=Tsqk4DGa1oOnphUS24zwE6-4EZMo0sqztye1sNwnLN4,4822
pendulum/locales/en/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
pendulum/locales/en/__pycache__/__init__.cpython-311.pyc,,
pendulum/locales/en/__pycache__/custom.cpython-311.pyc,,
pendulum/locales/en/__pycache__/locale.cpython-311.pyc,,
pendulum/locales/en/custom.py,sha256=PW4F4dcPoKjEbfzHLQalW66WXDtaLSOIR7SjbyaZGk8,636
pendulum/locales/en/locale.py,sha256=wbq_f5YbuKrDKaRB32982W21FYJd7ix00LE1mInlFH8,5064
pendulum/locales/es/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
pendulum/locales/es/__pycache__/__init__.cpython-311.pyc,,
pendulum/locales/es/__pycache__/custom.cpython-311.pyc,,
pendulum/locales/es/__pycache__/locale.cpython-311.pyc,,
pendulum/locales/es/custom.py,sha256=17h3Y5HqdxPPdqMheG7Hn29WrXvHs6HqGKlTO_sn1YM,628
pendulum/locales/es/locale.py,sha256=j6fmEM5uOivo1PKePKqgnjG7Dj2gIeuuKxEN79Ekals,4871
pendulum/locales/fa/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
pendulum/locales/fa/__pycache__/__init__.cpython-311.pyc,,
pendulum/locales/fa/__pycache__/custom.cpython-311.pyc,,
pendulum/locales/fa/__pycache__/locale.cpython-311.pyc,,
pendulum/locales/fa/custom.py,sha256=yuU6s6evM1XFCXmuE4QjHW5RwiwltSy1xhRryF_XsUU,455
pendulum/locales/fa/locale.py,sha256=_-K24CSVZ2OtelErgDfeFHzvddAPYx5u2FC8KvWjACo,5112
pendulum/locales/fo/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
pendulum/locales/fo/__pycache__/__init__.cpython-311.pyc,,
pendulum/locales/fo/__pycache__/custom.cpython-311.pyc,,
pendulum/locales/fo/__pycache__/locale.cpython-311.pyc,,
pendulum/locales/fo/custom.py,sha256=fMY9tEmLB07Bbp2MkwTcMW28a3koDsHfMHvb7NeCc4Q,499
pendulum/locales/fo/locale.py,sha256=UyxR9DM5Ak89BCMSQVl5zctTuQaQHGOqY_iYr_ARd2s,4555
pendulum/locales/fr/__init__.py,sha256=lVL4VPz3c4kWCgjPtL4uuQLNmgwK92q1ffAsv5El02k,25
pendulum/locales/fr/__pycache__/__init__.cpython-311.pyc,,
pendulum/locales/fr/__pycache__/custom.cpython-311.pyc,,
pendulum/locales/fr/__pycache__/locale.cpython-311.pyc,,
pendulum/locales/fr/custom.py,sha256=kAfAr-7FaLIocIrFZPUqZ7kfO833OCbJVF3ypzN5Oqg,612
pendulum/locales/fr/locale.py,sha256=nDdBuZ0EciY0dpKWDykp_v1y0PZWFL3U2sMg2gSZ3z4,4704
pendulum/locales/id/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
pendulum/locales/id/__pycache__/__init__.cpython-311.pyc,,
pendulum/locales/id/__pycache__/custom.cpython-311.pyc,,
pendulum/locales/id/__pycache__/locale.cpython-311.pyc,,
pendulum/locales/id/custom.py,sha256=uChc_mPCfYxsM4tw0pQvif31YsExoDcTeva2cWjLu9g,523
pendulum/locales/id/locale.py,sha256=7Dqfxb5FIoi8UdSrlpUawo40TWAI5awzE3JRmj0xHHc,4184
pendulum/locales/it/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
pendulum/locales/it/__pycache__/__init__.cpython-311.pyc,,
pendulum/locales/it/__pycache__/custom.cpython-311.pyc,,
pendulum/locales/it/__pycache__/locale.cpython-311.pyc,,
pendulum/locales/it/custom.py,sha256=nmCVmVTx0B6lknX4UblbsS-S8WJhhsob3kMgUZiYKp8,600
pendulum/locales/it/locale.py,sha256=pNRseajglYWnlddr7vAn-9C6Q3pu8u4c6-9t7ABRNqY,4861
pendulum/locales/ko/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
pendulum/locales/ko/__pycache__/__init__.cpython-311.pyc,,
pendulum/locales/ko/__pycache__/custom.cpython-311.pyc,,
pendulum/locales/ko/__pycache__/locale.cpython-311.pyc,,
pendulum/locales/ko/custom.py,sha256=GdSQMb-qQ3aIkdoPpW9ff-iycDWSFmuJTMfrjJbiHcU,484
pendulum/locales/ko/locale.py,sha256=1SB49DRkj47OcA1ZMJE4xH3t_ASCIa-t2nK4sbbPmOI,3579
pendulum/locales/locale.py,sha256=ESQrnDsdyYsFvgyuX3lkJ6tKHG4IezQk-oe_x6ij3cQ,3131
pendulum/locales/lt/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
pendulum/locales/lt/__pycache__/__init__.cpython-311.pyc,,
pendulum/locales/lt/__pycache__/custom.cpython-311.pyc,,
pendulum/locales/lt/__pycache__/locale.cpython-311.pyc,,
pendulum/locales/lt/custom.py,sha256=EipZptVq0ATZQzCgiGcrMEhvM06m8Fon-KSrcCKReAI,3574
pendulum/locales/lt/locale.py,sha256=AUBDrdCuQY7_ln4rowzlcU22xQ0EHMWqffc8jqQhG5Q,8469
pendulum/locales/nb/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
pendulum/locales/nb/__pycache__/__init__.cpython-311.pyc,,
pendulum/locales/nb/__pycache__/custom.cpython-311.pyc,,
pendulum/locales/nb/__pycache__/locale.cpython-311.pyc,,
pendulum/locales/nb/custom.py,sha256=B2rQwzo6hVOf_YFhWEVx7Lr9k6WDzVKG9FG3XbDxOIE,530
pendulum/locales/nb/locale.py,sha256=XD0_QdZtGh1Oo0U6zh7hbFiQb5lFzwNpFfcjMXfdYAY,4986
pendulum/locales/nl/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
pendulum/locales/nl/__pycache__/__init__.cpython-311.pyc,,
pendulum/locales/nl/__pycache__/custom.cpython-311.pyc,,
pendulum/locales/nl/__pycache__/locale.cpython-311.pyc,,
pendulum/locales/nl/custom.py,sha256=dJXiYSTCtIqBYWH74osacEWV3VeqRubIOGb5_XXTR6U,596
pendulum/locales/nl/locale.py,sha256=RPQm-GAF-W1xafjWREIqRTgnclq7qP9QBoXtCFvaNlQ,4666
pendulum/locales/nn/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
pendulum/locales/nn/__pycache__/__init__.cpython-311.pyc,,
pendulum/locales/nn/__pycache__/custom.cpython-311.pyc,,
pendulum/locales/nn/__pycache__/locale.cpython-311.pyc,,
pendulum/locales/nn/custom.py,sha256=B2rQwzo6hVOf_YFhWEVx7Lr9k6WDzVKG9FG3XbDxOIE,530
pendulum/locales/nn/locale.py,sha256=NryH5sNUnpAdCJ01mST4OrGgpr6cLaRKu5Oi2W__aa0,4726
pendulum/locales/pl/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
pendulum/locales/pl/__pycache__/__init__.cpython-311.pyc,,
pendulum/locales/pl/__pycache__/custom.cpython-311.pyc,,
pendulum/locales/pl/__pycache__/locale.cpython-311.pyc,,
pendulum/locales/pl/custom.py,sha256=8m_v2Ynv5xMq-EmJrTXdIQkTb3oltLwYhmqFvpOPHVc,537
pendulum/locales/pl/locale.py,sha256=8Q25uqfExrMEmob3Qx7NuAfW4dDSnj8zRx4ADZVs_2A,8891
pendulum/locales/pt_br/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
pendulum/locales/pt_br/__pycache__/__init__.cpython-311.pyc,,
pendulum/locales/pt_br/__pycache__/custom.cpython-311.pyc,,
pendulum/locales/pt_br/__pycache__/locale.cpython-311.pyc,,
pendulum/locales/pt_br/custom.py,sha256=eWVXefOKashQzV4ypzGRpzWznD3R4RJvp73SFhPC5LA,491
pendulum/locales/pt_br/locale.py,sha256=GTotVS2NJkKJiq8aoYKFjP-BKY_7HS5jnxON0UKOhQQ,4792
pendulum/locales/ru/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
pendulum/locales/ru/__pycache__/__init__.cpython-311.pyc,,
pendulum/locales/ru/__pycache__/custom.cpython-311.pyc,,
pendulum/locales/ru/__pycache__/locale.cpython-311.pyc,,
pendulum/locales/ru/custom.py,sha256=lbfr7fypGvYUemcGy7A6SEFTdeUZE9Ug9oOkUdWOoU8,526
pendulum/locales/ru/locale.py,sha256=iRrqMMfWMThae3opA4Mnaz-36TxBnQhnKSLZBsnGp_0,9685
pendulum/locales/zh/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
pendulum/locales/zh/__pycache__/__init__.cpython-311.pyc,,
pendulum/locales/zh/__pycache__/custom.cpython-311.pyc,,
pendulum/locales/zh/__pycache__/locale.cpython-311.pyc,,
pendulum/locales/zh/custom.py,sha256=Vmtm94Bqbn1vVgOq036Rl9DV__5D2uOnxyCD-XCFEo8,470
pendulum/locales/zh/locale.py,sha256=lD_WDMQyI7dfftcZJv4it7HjhFoEfOTUWVpqdY_GvzM,3751
pendulum/mixins/__init__.py,sha256=lVL4VPz3c4kWCgjPtL4uuQLNmgwK92q1ffAsv5El02k,25
pendulum/mixins/__pycache__/__init__.cpython-311.pyc,,
pendulum/mixins/__pycache__/default.cpython-311.pyc,,
pendulum/mixins/default.py,sha256=JcuObnFwaFvmnQKRjw90lJSFYM_wsjAZppt5_rDC0hs,945
pendulum/parser.py,sha256=-G-VT1vjGVzg1iEhp6_dcmi-SJ3RN6_x7oyatNzt6Bw,3593
pendulum/parsing/__init__.py,sha256=QqkgrQDXb6S3kKwr5CYj2OV5vn6FlykFlOek4OXZRU8,5591
pendulum/parsing/__pycache__/__init__.cpython-311.pyc,,
pendulum/parsing/__pycache__/iso8601.cpython-311.pyc,,
pendulum/parsing/_iso8601.c,sha256=mNSd5NwIRb_yBI8l_OuoZQRBkXevLrpsESRnjcKkiWg,38384
pendulum/parsing/exceptions/__init__.py,sha256=HOFxWKLvXl8WGjVHe_IpmqXRI3zedWdTu_0eMuopioQ,44
pendulum/parsing/exceptions/__pycache__/__init__.cpython-311.pyc,,
pendulum/parsing/iso8601.py,sha256=Nan53RPxB60BUX_5tABS_qYo7pQIq6-e3HArYhQC_NY,14304
pendulum/period.py,sha256=XAVl-W4jqVmSPSjlrL9i2ocomVncGfTGeQONjc9RIqE,11175
pendulum/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
pendulum/time.py,sha256=y5TwExt2L0qUzcCw5RYsMdBkpNXgPzRpR27y6cQd7R8,8125
pendulum/tz/__init__.py,sha256=A-_5bN29l2MBXXJ6moSLOmDerTyCXLCIOqyFFHRxcfs,1369
pendulum/tz/__pycache__/__init__.cpython-311.pyc,,
pendulum/tz/__pycache__/exceptions.cpython-311.pyc,,
pendulum/tz/__pycache__/local_timezone.cpython-311.pyc,,
pendulum/tz/__pycache__/timezone.cpython-311.pyc,,
pendulum/tz/data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
pendulum/tz/data/__pycache__/__init__.cpython-311.pyc,,
pendulum/tz/data/__pycache__/windows.cpython-311.pyc,,
pendulum/tz/data/windows.py,sha256=3wfke5bjhvmlqvVFJX4ZuQjBOqVBW5ybeS-yTrj5r58,6687
pendulum/tz/exceptions.py,sha256=xQlROtKUV2xCbWYP0uNu3xLLA_HN_DT01iSxhibFvTQ,492
pendulum/tz/local_timezone.py,sha256=D-vPQ3MkNFPZcmMRd7zuA4-8ooi2tkl8ENL8s6K58uw,8095
pendulum/tz/timezone.py,sha256=UJi1hoFRDrmXU7hv6B8QSy-vfe-ZeeDNMwJHda36UG4,11528
pendulum/tz/zoneinfo/__init__.py,sha256=tce8_9Rc3ELUXU0KAmUBC5-y4Duciu7wPkCrCgBSiYk,440
pendulum/tz/zoneinfo/__pycache__/__init__.cpython-311.pyc,,
pendulum/tz/zoneinfo/__pycache__/exceptions.cpython-311.pyc,,
pendulum/tz/zoneinfo/__pycache__/posix_timezone.cpython-311.pyc,,
pendulum/tz/zoneinfo/__pycache__/reader.cpython-311.pyc,,
pendulum/tz/zoneinfo/__pycache__/timezone.cpython-311.pyc,,
pendulum/tz/zoneinfo/__pycache__/transition.cpython-311.pyc,,
pendulum/tz/zoneinfo/__pycache__/transition_type.cpython-311.pyc,,
pendulum/tz/zoneinfo/exceptions.py,sha256=XfTTFMOg5bZCWR8eAgOLPoybO9lh8xfv3443936FW5g,425
pendulum/tz/zoneinfo/posix_timezone.py,sha256=io9cLnibYatE8YyDuqEOcb2dlGjj8bZGEsXMBZesES8,7277
pendulum/tz/zoneinfo/reader.py,sha256=wpTGU8ETc-mUAMBiZgTiUB4eDWaF_9HGLoppuBz1tjM,6852
pendulum/tz/zoneinfo/timezone.py,sha256=rPz_6i032_p0ZmN8j9w_KVqbpZ3mQtQmlkz4vX3SB7s,4248
pendulum/tz/zoneinfo/transition.py,sha256=kGFAVy-zejKgvZ2AJMEB6vaPO2PuZMyGs992GxEtQ9E,2055
pendulum/tz/zoneinfo/transition_type.py,sha256=6mFOT4BDVTqLqH6b0bwxlz3BWHgH17SMk9QFEKSoMG8,894
pendulum/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
pendulum/utils/__pycache__/__init__.cpython-311.pyc,,
pendulum/utils/__pycache__/_compat.cpython-311.pyc,,
pendulum/utils/_compat.py,sha256=bEkROlv5IBuz6RcSdgPxweNpjYvluBXvHt1qWGaS5T0,1263

View File

@ -0,0 +1,4 @@
Wheel-Version: 1.0
Generator: poetry-core 1.6.0
Root-Is-Purelib: false
Tag: cp311-cp311-manylinux_2_36_x86_64

View File

@ -0,0 +1,315 @@
from __future__ import absolute_import
import datetime as _datetime
from typing import Optional
from typing import Union
from .__version__ import __version__
from .constants import DAYS_PER_WEEK
from .constants import FRIDAY
from .constants import HOURS_PER_DAY
from .constants import MINUTES_PER_HOUR
from .constants import MONDAY
from .constants import MONTHS_PER_YEAR
from .constants import SATURDAY
from .constants import SECONDS_PER_DAY
from .constants import SECONDS_PER_HOUR
from .constants import SECONDS_PER_MINUTE
from .constants import SUNDAY
from .constants import THURSDAY
from .constants import TUESDAY
from .constants import WEDNESDAY
from .constants import WEEKS_PER_YEAR
from .constants import YEARS_PER_CENTURY
from .constants import YEARS_PER_DECADE
from .date import Date
from .datetime import DateTime
from .duration import Duration
from .formatting import Formatter
from .helpers import format_diff
from .helpers import get_locale
from .helpers import get_test_now
from .helpers import has_test_now
from .helpers import locale
from .helpers import set_locale
from .helpers import set_test_now
from .helpers import test
from .helpers import week_ends_at
from .helpers import week_starts_at
from .parser import parse
from .period import Period
from .time import Time
from .tz import POST_TRANSITION
from .tz import PRE_TRANSITION
from .tz import TRANSITION_ERROR
from .tz import UTC
from .tz import local_timezone
from .tz import set_local_timezone
from .tz import test_local_timezone
from .tz import timezone
from .tz import timezones
from .tz.timezone import Timezone as _Timezone
from .utils._compat import _HAS_FOLD
_TEST_NOW = None # type: Optional[DateTime]
_LOCALE = "en"
_WEEK_STARTS_AT = MONDAY
_WEEK_ENDS_AT = SUNDAY
_formatter = Formatter()
def _safe_timezone(obj):
# type: (Optional[Union[str, float, _datetime.tzinfo, _Timezone]]) -> _Timezone
"""
Creates a timezone instance
from a string, Timezone, TimezoneInfo or integer offset.
"""
if isinstance(obj, _Timezone):
return obj
if obj is None or obj == "local":
return local_timezone()
if isinstance(obj, (int, float)):
obj = int(obj * 60 * 60)
elif isinstance(obj, _datetime.tzinfo):
# pytz
if hasattr(obj, "localize"):
obj = obj.zone
elif obj.tzname(None) == "UTC":
return UTC
else:
offset = obj.utcoffset(None)
if offset is None:
offset = _datetime.timedelta(0)
obj = int(offset.total_seconds())
return timezone(obj)
# Public API
def datetime(
year, # type: int
month, # type: int
day, # type: int
hour=0, # type: int
minute=0, # type: int
second=0, # type: int
microsecond=0, # type: int
tz=UTC, # type: Optional[Union[str, float, _Timezone]]
dst_rule=POST_TRANSITION, # type: str
): # type: (...) -> DateTime
"""
Creates a new DateTime instance from a specific date and time.
"""
if tz is not None:
tz = _safe_timezone(tz)
if not _HAS_FOLD:
dt = naive(year, month, day, hour, minute, second, microsecond)
else:
dt = _datetime.datetime(year, month, day, hour, minute, second, microsecond)
if tz is not None:
dt = tz.convert(dt, dst_rule=dst_rule)
return DateTime(
dt.year,
dt.month,
dt.day,
dt.hour,
dt.minute,
dt.second,
dt.microsecond,
tzinfo=dt.tzinfo,
fold=dt.fold,
)
def local(
year, month, day, hour=0, minute=0, second=0, microsecond=0
): # type: (int, int, int, int, int, int, int) -> DateTime
"""
Return a DateTime in the local timezone.
"""
return datetime(
year, month, day, hour, minute, second, microsecond, tz=local_timezone()
)
def naive(
year, month, day, hour=0, minute=0, second=0, microsecond=0
): # type: (int, int, int, int, int, int, int) -> DateTime
"""
Return a naive DateTime.
"""
return DateTime(year, month, day, hour, minute, second, microsecond)
def date(year, month, day): # type: (int, int, int) -> Date
"""
Create a new Date instance.
"""
return Date(year, month, day)
def time(hour, minute=0, second=0, microsecond=0): # type: (int, int, int, int) -> Time
"""
Create a new Time instance.
"""
return Time(hour, minute, second, microsecond)
def instance(
dt, tz=UTC
): # type: (_datetime.datetime, Optional[Union[str, _Timezone]]) -> DateTime
"""
Create a DateTime instance from a datetime one.
"""
if not isinstance(dt, _datetime.datetime):
raise ValueError("instance() only accepts datetime objects.")
if isinstance(dt, DateTime):
return dt
tz = dt.tzinfo or tz
# Checking for pytz/tzinfo
if isinstance(tz, _datetime.tzinfo) and not isinstance(tz, _Timezone):
# pytz
if hasattr(tz, "localize") and tz.zone:
tz = tz.zone
else:
# We have no sure way to figure out
# the timezone name, we fallback
# on a fixed offset
tz = tz.utcoffset(dt).total_seconds() / 3600
return datetime(
dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, dt.microsecond, tz=tz
)
def now(tz=None): # type: (Optional[Union[str, _Timezone]]) -> DateTime
"""
Get a DateTime instance for the current date and time.
"""
if has_test_now():
test_instance = get_test_now()
_tz = _safe_timezone(tz)
if tz is not None and _tz != test_instance.timezone:
test_instance = test_instance.in_tz(_tz)
return test_instance
if tz is None or tz == "local":
dt = _datetime.datetime.now(local_timezone())
elif tz is UTC or tz == "UTC":
dt = _datetime.datetime.now(UTC)
else:
dt = _datetime.datetime.now(UTC)
tz = _safe_timezone(tz)
dt = tz.convert(dt)
return DateTime(
dt.year,
dt.month,
dt.day,
dt.hour,
dt.minute,
dt.second,
dt.microsecond,
tzinfo=dt.tzinfo,
fold=dt.fold if _HAS_FOLD else 0,
)
def today(tz="local"): # type: (Union[str, _Timezone]) -> DateTime
"""
Create a DateTime instance for today.
"""
return now(tz).start_of("day")
def tomorrow(tz="local"): # type: (Union[str, _Timezone]) -> DateTime
"""
Create a DateTime instance for today.
"""
return today(tz).add(days=1)
def yesterday(tz="local"): # type: (Union[str, _Timezone]) -> DateTime
"""
Create a DateTime instance for today.
"""
return today(tz).subtract(days=1)
def from_format(
string, fmt, tz=UTC, locale=None, # noqa
): # type: (str, str, Union[str, _Timezone], Optional[str]) -> DateTime
"""
Creates a DateTime instance from a specific format.
"""
parts = _formatter.parse(string, fmt, now(), locale=locale)
if parts["tz"] is None:
parts["tz"] = tz
return datetime(**parts)
def from_timestamp(
timestamp, tz=UTC
): # type: (Union[int, float], Union[str, _Timezone]) -> DateTime
"""
Create a DateTime instance from a timestamp.
"""
dt = _datetime.datetime.utcfromtimestamp(timestamp)
dt = datetime(
dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, dt.microsecond
)
if tz is not UTC or tz != "UTC":
dt = dt.in_timezone(tz)
return dt
def duration(
days=0, # type: float
seconds=0, # type: float
microseconds=0, # type: float
milliseconds=0, # type: float
minutes=0, # type: float
hours=0, # type: float
weeks=0, # type: float
years=0, # type: float
months=0, # type: float
): # type: (...) -> Duration
"""
Create a Duration instance.
"""
return Duration(
days=days,
seconds=seconds,
microseconds=microseconds,
milliseconds=milliseconds,
minutes=minutes,
hours=hours,
weeks=weeks,
years=years,
months=months,
)
def period(start, end, absolute=False): # type: (DateTime, DateTime, bool) -> Period
"""
Create a Period instance.
"""
return Period(start, end, absolute=absolute)

View File

@ -0,0 +1,930 @@
/* ------------------------------------------------------------------------- */
#include <Python.h>
#include <datetime.h>
#include <structmember.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
/* ------------------------------------------------------------------------- */
#define EPOCH_YEAR 1970
#define DAYS_PER_N_YEAR 365
#define DAYS_PER_L_YEAR 366
#define USECS_PER_SEC 1000000
#define SECS_PER_MIN 60
#define SECS_PER_HOUR (60 * SECS_PER_MIN)
#define SECS_PER_DAY (SECS_PER_HOUR * 24)
// 400-year chunks always have 146097 days (20871 weeks).
#define DAYS_PER_400_YEARS 146097L
#define SECS_PER_400_YEARS ((int64_t)DAYS_PER_400_YEARS * (int64_t)SECS_PER_DAY)
// The number of seconds in an aligned 100-year chunk, for those that
// do not begin with a leap year and those that do respectively.
const int64_t SECS_PER_100_YEARS[2] = {
(uint64_t)(76L * DAYS_PER_N_YEAR + 24L * DAYS_PER_L_YEAR) * SECS_PER_DAY,
(uint64_t)(75L * DAYS_PER_N_YEAR + 25L * DAYS_PER_L_YEAR) * SECS_PER_DAY};
// The number of seconds in an aligned 4-year chunk, for those that
// do not begin with a leap year and those that do respectively.
const int32_t SECS_PER_4_YEARS[2] = {
(4 * DAYS_PER_N_YEAR + 0 * DAYS_PER_L_YEAR) * SECS_PER_DAY,
(3 * DAYS_PER_N_YEAR + 1 * DAYS_PER_L_YEAR) * SECS_PER_DAY};
// The number of seconds in non-leap and leap years respectively.
const int32_t SECS_PER_YEAR[2] = {
DAYS_PER_N_YEAR * SECS_PER_DAY,
DAYS_PER_L_YEAR *SECS_PER_DAY};
#define MONTHS_PER_YEAR 12
// The month lengths in non-leap and leap years respectively.
const int32_t DAYS_PER_MONTHS[2][13] = {
{-1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
{-1, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}};
// The day offsets of the beginning of each (1-based) month in non-leap
// and leap years respectively.
// For example, in a leap year there are 335 days before December.
const int32_t MONTHS_OFFSETS[2][14] = {
{-1, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
{-1, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}};
const int DAY_OF_WEEK_TABLE[12] = {
0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
#define TM_SUNDAY 0
#define TM_MONDAY 1
#define TM_TUESDAY 2
#define TM_WEDNESDAY 3
#define TM_THURSDAY 4
#define TM_FRIDAY 5
#define TM_SATURDAY 6
#define TM_JANUARY 0
#define TM_FEBRUARY 1
#define TM_MARCH 2
#define TM_APRIL 3
#define TM_MAY 4
#define TM_JUNE 5
#define TM_JULY 6
#define TM_AUGUST 7
#define TM_SEPTEMBER 8
#define TM_OCTOBER 9
#define TM_NOVEMBER 10
#define TM_DECEMBER 11
/* ------------------------------------------------------------------------- */
int _p(int y)
{
return y + y / 4 - y / 100 + y / 400;
}
int _is_leap(int year)
{
return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
}
int _is_long_year(int year)
{
return (_p(year) % 7 == 4) || (_p(year - 1) % 7 == 3);
}
int _week_day(int year, int month, int day)
{
int y;
int w;
y = year - (month < 3);
w = (_p(y) + DAY_OF_WEEK_TABLE[month - 1] + day) % 7;
if (!w)
{
w = 7;
}
return w;
}
int _days_in_year(int year)
{
if (_is_leap(year))
{
return DAYS_PER_L_YEAR;
}
return DAYS_PER_N_YEAR;
}
int _day_number(int year, int month, int day)
{
month = (month + 9) % 12;
year = year - month / 10;
return (
365 * year + year / 4 - year / 100 + year / 400 + (month * 306 + 5) / 10 + (day - 1));
}
int _get_offset(PyObject *dt)
{
PyObject *tzinfo;
PyObject *offset;
tzinfo = ((PyDateTime_DateTime *)(dt))->tzinfo;
if (tzinfo != Py_None)
{
offset = PyObject_CallMethod(tzinfo, "utcoffset", "O", dt);
return PyDateTime_DELTA_GET_DAYS(offset) * SECS_PER_DAY + PyDateTime_DELTA_GET_SECONDS(offset);
}
return 0;
}
int _has_tzinfo(PyObject *dt)
{
return ((_PyDateTime_BaseTZInfo *)(dt))->hastzinfo;
}
char *_get_tz_name(PyObject *dt)
{
PyObject *tzinfo;
char *tz = "";
tzinfo = ((PyDateTime_DateTime *)(dt))->tzinfo;
if (tzinfo != Py_None)
{
if (PyObject_HasAttrString(tzinfo, "name"))
{
// Pendulum timezone
tz = (char *)PyUnicode_AsUTF8(
PyObject_GetAttrString(tzinfo, "name"));
}
else if (PyObject_HasAttrString(tzinfo, "zone"))
{
// pytz timezone
tz = (char *)PyUnicode_AsUTF8(
PyObject_GetAttrString(tzinfo, "zone"));
}
}
return tz;
}
/* ------------------------ Custom Types ------------------------------- */
/*
* class Diff():
*/
typedef struct
{
PyObject_HEAD int years;
int months;
int days;
int hours;
int minutes;
int seconds;
int microseconds;
int total_days;
} Diff;
/*
* def __init__(self, years, months, days, hours, minutes, seconds, microseconds, total_days):
* self.years = years
* # ...
*/
static int Diff_init(Diff *self, PyObject *args, PyObject *kwargs)
{
int years;
int months;
int days;
int hours;
int minutes;
int seconds;
int microseconds;
int total_days;
if (!PyArg_ParseTuple(args, "iiiiiii", &years, &months, &days, &hours, &minutes, &seconds, &microseconds, &total_days))
return -1;
self->years = years;
self->months = months;
self->days = days;
self->hours = hours;
self->minutes = minutes;
self->seconds = seconds;
self->microseconds = microseconds;
self->total_days = total_days;
return 0;
}
/*
* def __repr__(self):
* return '{} years {} months {} days {} hours {} minutes {} seconds {} microseconds'.format(
* self.years, self.months, self.days, self.minutes, self.hours, self.seconds, self.microseconds
* )
*/
static PyObject *Diff_repr(Diff *self)
{
char repr[82] = {0};
sprintf(
repr,
"%d years %d months %d days %d hours %d minutes %d seconds %d microseconds",
self->years,
self->months,
self->days,
self->hours,
self->minutes,
self->seconds,
self->microseconds);
return PyUnicode_FromString(repr);
}
/*
* Instantiate new Diff_type object
* Skip overhead of calling PyObject_New and PyObject_Init.
* Directly allocate object.
*/
static PyObject *new_diff_ex(int years, int months, int days, int hours, int minutes, int seconds, int microseconds, int total_days, PyTypeObject *type)
{
Diff *self = (Diff *)(type->tp_alloc(type, 0));
if (self != NULL)
{
self->years = years;
self->months = months;
self->days = days;
self->hours = hours;
self->minutes = minutes;
self->seconds = seconds;
self->microseconds = microseconds;
self->total_days = total_days;
}
return (PyObject *)self;
}
/*
* Class member / class attributes
*/
static PyMemberDef Diff_members[] = {
{"years", T_INT, offsetof(Diff, years), 0, "years in diff"},
{"months", T_INT, offsetof(Diff, months), 0, "months in diff"},
{"days", T_INT, offsetof(Diff, days), 0, "days in diff"},
{"hours", T_INT, offsetof(Diff, hours), 0, "hours in diff"},
{"minutes", T_INT, offsetof(Diff, minutes), 0, "minutes in diff"},
{"seconds", T_INT, offsetof(Diff, seconds), 0, "seconds in diff"},
{"microseconds", T_INT, offsetof(Diff, microseconds), 0, "microseconds in diff"},
{"total_days", T_INT, offsetof(Diff, total_days), 0, "total days in diff"},
{NULL}};
static PyTypeObject Diff_type = {
PyVarObject_HEAD_INIT(NULL, 0) "PreciseDiff", /* tp_name */
sizeof(Diff), /* tp_basicsize */
0, /* tp_itemsize */
0, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_as_async */
(reprfunc)Diff_repr, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
(reprfunc)Diff_repr, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
"Precise difference between two datetime objects", /* tp_doc */
};
#define new_diff(years, months, days, hours, minutes, seconds, microseconds, total_days) new_diff_ex(years, months, days, hours, minutes, seconds, microseconds, total_days, &Diff_type)
/* -------------------------- Functions --------------------------*/
PyObject *is_leap(PyObject *self, PyObject *args)
{
PyObject *leap;
int year;
if (!PyArg_ParseTuple(args, "i", &year))
{
PyErr_SetString(
PyExc_ValueError, "Invalid parameters");
return NULL;
}
leap = PyBool_FromLong(_is_leap(year));
return leap;
}
PyObject *is_long_year(PyObject *self, PyObject *args)
{
PyObject *is_long;
int year;
if (!PyArg_ParseTuple(args, "i", &year))
{
PyErr_SetString(
PyExc_ValueError, "Invalid parameters");
return NULL;
}
is_long = PyBool_FromLong(_is_long_year(year));
return is_long;
}
PyObject *week_day(PyObject *self, PyObject *args)
{
PyObject *wd;
int year;
int month;
int day;
if (!PyArg_ParseTuple(args, "iii", &year, &month, &day))
{
PyErr_SetString(
PyExc_ValueError, "Invalid parameters");
return NULL;
}
wd = PyLong_FromLong(_week_day(year, month, day));
return wd;
}
PyObject *days_in_year(PyObject *self, PyObject *args)
{
PyObject *ndays;
int year;
if (!PyArg_ParseTuple(args, "i", &year))
{
PyErr_SetString(
PyExc_ValueError, "Invalid parameters");
return NULL;
}
ndays = PyLong_FromLong(_days_in_year(year));
return ndays;
}
PyObject *timestamp(PyObject *self, PyObject *args)
{
int64_t result;
PyObject *dt;
if (!PyArg_ParseTuple(args, "O", &dt))
{
PyErr_SetString(
PyExc_ValueError, "Invalid parameters");
return NULL;
}
int year = (double)PyDateTime_GET_YEAR(dt);
int month = PyDateTime_GET_MONTH(dt);
int day = PyDateTime_GET_DAY(dt);
int hour = PyDateTime_DATE_GET_HOUR(dt);
int minute = PyDateTime_DATE_GET_MINUTE(dt);
int second = PyDateTime_DATE_GET_SECOND(dt);
result = (year - 1970) * 365 + MONTHS_OFFSETS[0][month];
result += (int)floor((double)(year - 1968) / 4);
result -= (year - 1900) / 100;
result += (year - 1600) / 400;
if (_is_leap(year) && month < 3)
{
result -= 1;
}
result += day - 1;
result *= 24;
result += hour;
result *= 60;
result += minute;
result *= 60;
result += second;
return PyLong_FromSsize_t(result);
}
PyObject *local_time(PyObject *self, PyObject *args)
{
double unix_time;
int32_t utc_offset;
int32_t year;
int32_t microsecond;
int64_t seconds;
int32_t leap_year;
int64_t sec_per_100years;
int64_t sec_per_4years;
int32_t sec_per_year;
int32_t month;
int32_t day;
int32_t month_offset;
int32_t hour;
int32_t minute;
int32_t second;
if (!PyArg_ParseTuple(args, "dii", &unix_time, &utc_offset, &microsecond))
{
PyErr_SetString(
PyExc_ValueError, "Invalid parameters");
return NULL;
}
year = EPOCH_YEAR;
seconds = (int64_t)floor(unix_time);
// Shift to a base year that is 400-year aligned.
if (seconds >= 0)
{
seconds -= 10957L * SECS_PER_DAY;
year += 30; // == 2000;
}
else
{
seconds += (int64_t)(146097L - 10957L) * SECS_PER_DAY;
year -= 370; // == 1600;
}
seconds += utc_offset;
// Handle years in chunks of 400/100/4/1
year += 400 * (seconds / SECS_PER_400_YEARS);
seconds %= SECS_PER_400_YEARS;
if (seconds < 0)
{
seconds += SECS_PER_400_YEARS;
year -= 400;
}
leap_year = 1; // 4-century aligned
sec_per_100years = SECS_PER_100_YEARS[leap_year];
while (seconds >= sec_per_100years)
{
seconds -= sec_per_100years;
year += 100;
leap_year = 0; // 1-century, non 4-century aligned
sec_per_100years = SECS_PER_100_YEARS[leap_year];
}
sec_per_4years = SECS_PER_4_YEARS[leap_year];
while (seconds >= sec_per_4years)
{
seconds -= sec_per_4years;
year += 4;
leap_year = 1; // 4-year, non century aligned
sec_per_4years = SECS_PER_4_YEARS[leap_year];
}
sec_per_year = SECS_PER_YEAR[leap_year];
while (seconds >= sec_per_year)
{
seconds -= sec_per_year;
year += 1;
leap_year = 0; // non 4-year aligned
sec_per_year = SECS_PER_YEAR[leap_year];
}
// Handle months and days
month = TM_DECEMBER + 1;
day = seconds / SECS_PER_DAY + 1;
seconds %= SECS_PER_DAY;
while (month != TM_JANUARY + 1)
{
month_offset = MONTHS_OFFSETS[leap_year][month];
if (day > month_offset)
{
day -= month_offset;
break;
}
month -= 1;
}
// Handle hours, minutes and seconds
hour = seconds / SECS_PER_HOUR;
seconds %= SECS_PER_HOUR;
minute = seconds / SECS_PER_MIN;
second = seconds % SECS_PER_MIN;
return Py_BuildValue("NNNNNNN",
PyLong_FromLong(year),
PyLong_FromLong(month),
PyLong_FromLong(day),
PyLong_FromLong(hour),
PyLong_FromLong(minute),
PyLong_FromLong(second),
PyLong_FromLong(microsecond));
}
// Calculate a precise difference between two datetimes.
PyObject *precise_diff(PyObject *self, PyObject *args)
{
PyObject *dt1;
PyObject *dt2;
if (!PyArg_ParseTuple(args, "OO", &dt1, &dt2))
{
PyErr_SetString(
PyExc_ValueError, "Invalid parameters");
return NULL;
}
int year_diff = 0;
int month_diff = 0;
int day_diff = 0;
int hour_diff = 0;
int minute_diff = 0;
int second_diff = 0;
int microsecond_diff = 0;
int sign = 1;
int year;
int month;
int leap;
int days_in_last_month;
int days_in_month;
int dt1_year = PyDateTime_GET_YEAR(dt1);
int dt2_year = PyDateTime_GET_YEAR(dt2);
int dt1_month = PyDateTime_GET_MONTH(dt1);
int dt2_month = PyDateTime_GET_MONTH(dt2);
int dt1_day = PyDateTime_GET_DAY(dt1);
int dt2_day = PyDateTime_GET_DAY(dt2);
int dt1_hour = 0;
int dt2_hour = 0;
int dt1_minute = 0;
int dt2_minute = 0;
int dt1_second = 0;
int dt2_second = 0;
int dt1_microsecond = 0;
int dt2_microsecond = 0;
int dt1_total_seconds = 0;
int dt2_total_seconds = 0;
int dt1_offset = 0;
int dt2_offset = 0;
int dt1_is_datetime = PyDateTime_Check(dt1);
int dt2_is_datetime = PyDateTime_Check(dt2);
char *tz1 = "";
char *tz2 = "";
int in_same_tz = 0;
int total_days = (_day_number(dt2_year, dt2_month, dt2_day) - _day_number(dt1_year, dt1_month, dt1_day));
// If both dates are datetimes, we check
// If we are in the same timezone
if (dt1_is_datetime && dt2_is_datetime)
{
if (_has_tzinfo(dt1))
{
tz1 = _get_tz_name(dt1);
dt1_offset = _get_offset(dt1);
}
if (_has_tzinfo(dt2))
{
tz2 = _get_tz_name(dt2);
dt2_offset = _get_offset(dt2);
}
in_same_tz = tz1 == tz2 && strncmp(tz1, "", 1);
}
// If we have datetimes (and not only dates)
// we get the information we need
if (dt1_is_datetime)
{
dt1_hour = PyDateTime_DATE_GET_HOUR(dt1);
dt1_minute = PyDateTime_DATE_GET_MINUTE(dt1);
dt1_second = PyDateTime_DATE_GET_SECOND(dt1);
dt1_microsecond = PyDateTime_DATE_GET_MICROSECOND(dt1);
if ((!in_same_tz && dt1_offset != 0) || total_days == 0)
{
dt1_hour -= dt1_offset / SECS_PER_HOUR;
dt1_offset %= SECS_PER_HOUR;
dt1_minute -= dt1_offset / SECS_PER_MIN;
dt1_offset %= SECS_PER_MIN;
dt1_second -= dt1_offset;
if (dt1_second < 0)
{
dt1_second += 60;
dt1_minute -= 1;
}
else if (dt1_second > 60)
{
dt1_second -= 60;
dt1_minute += 1;
}
if (dt1_minute < 0)
{
dt1_minute += 60;
dt1_hour -= 1;
}
else if (dt1_minute > 60)
{
dt1_minute -= 60;
dt1_hour += 1;
}
if (dt1_hour < 0)
{
dt1_hour += 24;
dt1_day -= 1;
}
else if (dt1_hour > 24)
{
dt1_hour -= 24;
dt1_day += 1;
}
}
dt1_total_seconds = (dt1_hour * SECS_PER_HOUR + dt1_minute * SECS_PER_MIN + dt1_second);
}
if (dt2_is_datetime)
{
dt2_hour = PyDateTime_DATE_GET_HOUR(dt2);
dt2_minute = PyDateTime_DATE_GET_MINUTE(dt2);
dt2_second = PyDateTime_DATE_GET_SECOND(dt2);
dt2_microsecond = PyDateTime_DATE_GET_MICROSECOND(dt2);
if ((!in_same_tz && dt2_offset != 0) || total_days == 0)
{
dt2_hour -= dt2_offset / SECS_PER_HOUR;
dt2_offset %= SECS_PER_HOUR;
dt2_minute -= dt2_offset / SECS_PER_MIN;
dt2_offset %= SECS_PER_MIN;
dt2_second -= dt2_offset;
if (dt2_second < 0)
{
dt2_second += 60;
dt2_minute -= 1;
}
else if (dt2_second > 60)
{
dt2_second -= 60;
dt2_minute += 1;
}
if (dt2_minute < 0)
{
dt2_minute += 60;
dt2_hour -= 1;
}
else if (dt2_minute > 60)
{
dt2_minute -= 60;
dt2_hour += 1;
}
if (dt2_hour < 0)
{
dt2_hour += 24;
dt2_day -= 1;
}
else if (dt2_hour > 24)
{
dt2_hour -= 24;
dt2_day += 1;
}
}
dt2_total_seconds = (dt2_hour * SECS_PER_HOUR + dt2_minute * SECS_PER_MIN + dt2_second);
}
// Direct comparison between two datetimes does not work
// so we need to check by properties
int dt1_gt_dt2 = (dt1_year > dt2_year || (dt1_year == dt2_year && dt1_month > dt2_month) || (dt1_year == dt2_year && dt1_month == dt2_month && dt1_day > dt2_day) || (dt1_year == dt2_year && dt1_month == dt2_month && dt1_day == dt2_day && dt1_total_seconds > dt2_total_seconds) || (dt1_year == dt2_year && dt1_month == dt2_month && dt1_day == dt2_day && dt1_total_seconds == dt2_total_seconds && dt1_microsecond > dt2_microsecond));
if (dt1_gt_dt2)
{
PyObject *temp;
temp = dt1;
dt1 = dt2;
dt2 = temp;
sign = -1;
// Retrieving properties
dt1_year = PyDateTime_GET_YEAR(dt1);
dt2_year = PyDateTime_GET_YEAR(dt2);
dt1_month = PyDateTime_GET_MONTH(dt1);
dt2_month = PyDateTime_GET_MONTH(dt2);
dt1_day = PyDateTime_GET_DAY(dt1);
dt2_day = PyDateTime_GET_DAY(dt2);
if (dt2_is_datetime)
{
dt1_hour = PyDateTime_DATE_GET_HOUR(dt1);
dt1_minute = PyDateTime_DATE_GET_MINUTE(dt1);
dt1_second = PyDateTime_DATE_GET_SECOND(dt1);
dt1_microsecond = PyDateTime_DATE_GET_MICROSECOND(dt1);
}
if (dt1_is_datetime)
{
dt2_hour = PyDateTime_DATE_GET_HOUR(dt2);
dt2_minute = PyDateTime_DATE_GET_MINUTE(dt2);
dt2_second = PyDateTime_DATE_GET_SECOND(dt2);
dt2_microsecond = PyDateTime_DATE_GET_MICROSECOND(dt2);
}
total_days = (_day_number(dt2_year, dt2_month, dt2_day) - _day_number(dt1_year, dt1_month, dt1_day));
}
year_diff = dt2_year - dt1_year;
month_diff = dt2_month - dt1_month;
day_diff = dt2_day - dt1_day;
hour_diff = dt2_hour - dt1_hour;
minute_diff = dt2_minute - dt1_minute;
second_diff = dt2_second - dt1_second;
microsecond_diff = dt2_microsecond - dt1_microsecond;
if (microsecond_diff < 0)
{
microsecond_diff += 1e6;
second_diff -= 1;
}
if (second_diff < 0)
{
second_diff += 60;
minute_diff -= 1;
}
if (minute_diff < 0)
{
minute_diff += 60;
hour_diff -= 1;
}
if (hour_diff < 0)
{
hour_diff += 24;
day_diff -= 1;
}
if (day_diff < 0)
{
// If we have a difference in days,
// we have to check if they represent months
year = dt2_year;
month = dt2_month;
if (month == 1)
{
month = 12;
year -= 1;
}
else
{
month -= 1;
}
leap = _is_leap(year);
days_in_last_month = DAYS_PER_MONTHS[leap][month];
days_in_month = DAYS_PER_MONTHS[_is_leap(dt2_year)][dt2_month];
if (day_diff < days_in_month - days_in_last_month)
{
// We don't have a full month, we calculate days
if (days_in_last_month < dt1_day)
{
day_diff += dt1_day;
}
else
{
day_diff += days_in_last_month;
}
}
else if (day_diff == days_in_month - days_in_last_month)
{
// We have exactly a full month
// We remove the days difference
// and add one to the months difference
day_diff = 0;
month_diff += 1;
}
else
{
// We have a full month
day_diff += days_in_last_month;
}
month_diff -= 1;
}
if (month_diff < 0)
{
month_diff += 12;
year_diff -= 1;
}
return new_diff(
year_diff * sign,
month_diff * sign,
day_diff * sign,
hour_diff * sign,
minute_diff * sign,
second_diff * sign,
microsecond_diff * sign,
total_days * sign);
}
/* ------------------------------------------------------------------------- */
static PyMethodDef helpers_methods[] = {
{"is_leap",
(PyCFunction)is_leap,
METH_VARARGS,
PyDoc_STR("Checks if a year is a leap year.")},
{"is_long_year",
(PyCFunction)is_long_year,
METH_VARARGS,
PyDoc_STR("Checks if a year is a long year.")},
{"week_day",
(PyCFunction)week_day,
METH_VARARGS,
PyDoc_STR("Returns the weekday number.")},
{"days_in_year",
(PyCFunction)days_in_year,
METH_VARARGS,
PyDoc_STR("Returns the number of days in the given year.")},
{"timestamp",
(PyCFunction)timestamp,
METH_VARARGS,
PyDoc_STR("Returns the timestamp of the given datetime.")},
{"local_time",
(PyCFunction)local_time,
METH_VARARGS,
PyDoc_STR("Returns a UNIX time as a broken down time for a particular transition type.")},
{"precise_diff",
(PyCFunction)precise_diff,
METH_VARARGS,
PyDoc_STR("Calculate a precise difference between two datetimes.")},
{NULL}};
/* ------------------------------------------------------------------------- */
static struct PyModuleDef moduledef = {
PyModuleDef_HEAD_INIT,
"_helpers",
NULL,
-1,
helpers_methods,
NULL,
NULL,
NULL,
NULL,
};
PyMODINIT_FUNC
PyInit__helpers(void)
{
PyObject *module;
PyDateTime_IMPORT;
module = PyModule_Create(&moduledef);
if (module == NULL)
return NULL;
// Diff declaration
Diff_type.tp_new = PyType_GenericNew;
Diff_type.tp_members = Diff_members;
Diff_type.tp_init = (initproc)Diff_init;
if (PyType_Ready(&Diff_type) < 0)
return NULL;
PyModule_AddObject(module, "PreciseDiff", (PyObject *)&Diff_type);
return module;
}

View File

@ -0,0 +1,358 @@
import datetime
import math
import typing
from collections import namedtuple
from ..constants import DAY_OF_WEEK_TABLE
from ..constants import DAYS_PER_L_YEAR
from ..constants import DAYS_PER_MONTHS
from ..constants import DAYS_PER_N_YEAR
from ..constants import EPOCH_YEAR
from ..constants import MONTHS_OFFSETS
from ..constants import SECS_PER_4_YEARS
from ..constants import SECS_PER_100_YEARS
from ..constants import SECS_PER_400_YEARS
from ..constants import SECS_PER_DAY
from ..constants import SECS_PER_HOUR
from ..constants import SECS_PER_MIN
from ..constants import SECS_PER_YEAR
from ..constants import TM_DECEMBER
from ..constants import TM_JANUARY
class PreciseDiff(
namedtuple(
"PreciseDiff",
"years months days " "hours minutes seconds microseconds " "total_days",
)
):
def __repr__(self):
return (
"{years} years "
"{months} months "
"{days} days "
"{hours} hours "
"{minutes} minutes "
"{seconds} seconds "
"{microseconds} microseconds"
).format(
years=self.years,
months=self.months,
days=self.days,
hours=self.hours,
minutes=self.minutes,
seconds=self.seconds,
microseconds=self.microseconds,
)
def is_leap(year): # type: (int) -> bool
return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)
def is_long_year(year): # type: (int) -> bool
def p(y):
return y + y // 4 - y // 100 + y // 400
return p(year) % 7 == 4 or p(year - 1) % 7 == 3
def week_day(year, month, day): # type: (int, int, int) -> int
if month < 3:
year -= 1
w = (
year
+ year // 4
- year // 100
+ year // 400
+ DAY_OF_WEEK_TABLE[month - 1]
+ day
) % 7
if not w:
w = 7
return w
def days_in_year(year): # type: (int) -> int
if is_leap(year):
return DAYS_PER_L_YEAR
return DAYS_PER_N_YEAR
def timestamp(dt): # type: (datetime.datetime) -> int
year = dt.year
result = (year - 1970) * 365 + MONTHS_OFFSETS[0][dt.month]
result += (year - 1968) // 4
result -= (year - 1900) // 100
result += (year - 1600) // 400
if is_leap(year) and dt.month < 3:
result -= 1
result += dt.day - 1
result *= 24
result += dt.hour
result *= 60
result += dt.minute
result *= 60
result += dt.second
return result
def local_time(
unix_time, utc_offset, microseconds
): # type: (int, int, int) -> typing.Tuple[int, int, int, int, int, int, int]
"""
Returns a UNIX time as a broken down time
for a particular transition type.
:type unix_time: int
:type utc_offset: int
:type microseconds: int
:rtype: tuple
"""
year = EPOCH_YEAR
seconds = int(math.floor(unix_time))
# Shift to a base year that is 400-year aligned.
if seconds >= 0:
seconds -= 10957 * SECS_PER_DAY
year += 30 # == 2000
else:
seconds += (146097 - 10957) * SECS_PER_DAY
year -= 370 # == 1600
seconds += utc_offset
# Handle years in chunks of 400/100/4/1
year += 400 * (seconds // SECS_PER_400_YEARS)
seconds %= SECS_PER_400_YEARS
if seconds < 0:
seconds += SECS_PER_400_YEARS
year -= 400
leap_year = 1 # 4-century aligned
sec_per_100years = SECS_PER_100_YEARS[leap_year]
while seconds >= sec_per_100years:
seconds -= sec_per_100years
year += 100
leap_year = 0 # 1-century, non 4-century aligned
sec_per_100years = SECS_PER_100_YEARS[leap_year]
sec_per_4years = SECS_PER_4_YEARS[leap_year]
while seconds >= sec_per_4years:
seconds -= sec_per_4years
year += 4
leap_year = 1 # 4-year, non century aligned
sec_per_4years = SECS_PER_4_YEARS[leap_year]
sec_per_year = SECS_PER_YEAR[leap_year]
while seconds >= sec_per_year:
seconds -= sec_per_year
year += 1
leap_year = 0 # non 4-year aligned
sec_per_year = SECS_PER_YEAR[leap_year]
# Handle months and days
month = TM_DECEMBER + 1
day = seconds // SECS_PER_DAY + 1
seconds %= SECS_PER_DAY
while month != TM_JANUARY + 1:
month_offset = MONTHS_OFFSETS[leap_year][month]
if day > month_offset:
day -= month_offset
break
month -= 1
# Handle hours, minutes, seconds and microseconds
hour = seconds // SECS_PER_HOUR
seconds %= SECS_PER_HOUR
minute = seconds // SECS_PER_MIN
second = seconds % SECS_PER_MIN
return (year, month, day, hour, minute, second, microseconds)
def precise_diff(
d1, d2
): # type: (typing.Union[datetime.datetime, datetime.date], typing.Union[datetime.datetime, datetime.date]) -> PreciseDiff
"""
Calculate a precise difference between two datetimes.
:param d1: The first datetime
:type d1: datetime.datetime or datetime.date
:param d2: The second datetime
:type d2: datetime.datetime or datetime.date
:rtype: PreciseDiff
"""
sign = 1
if d1 == d2:
return PreciseDiff(0, 0, 0, 0, 0, 0, 0, 0)
tzinfo1 = d1.tzinfo if isinstance(d1, datetime.datetime) else None
tzinfo2 = d2.tzinfo if isinstance(d2, datetime.datetime) else None
if (
tzinfo1 is None
and tzinfo2 is not None
or tzinfo2 is None
and tzinfo1 is not None
):
raise ValueError(
"Comparison between naive and aware datetimes is not supported"
)
if d1 > d2:
d1, d2 = d2, d1
sign = -1
d_diff = 0
hour_diff = 0
min_diff = 0
sec_diff = 0
mic_diff = 0
total_days = _day_number(d2.year, d2.month, d2.day) - _day_number(
d1.year, d1.month, d1.day
)
in_same_tz = False
tz1 = None
tz2 = None
# Trying to figure out the timezone names
# If we can't find them, we assume different timezones
if tzinfo1 and tzinfo2:
if hasattr(tzinfo1, "name"):
# Pendulum timezone
tz1 = tzinfo1.name
elif hasattr(tzinfo1, "zone"):
# pytz timezone
tz1 = tzinfo1.zone
if hasattr(tzinfo2, "name"):
tz2 = tzinfo2.name
elif hasattr(tzinfo2, "zone"):
tz2 = tzinfo2.zone
in_same_tz = tz1 == tz2 and tz1 is not None
if isinstance(d2, datetime.datetime):
if isinstance(d1, datetime.datetime):
# If we are not in the same timezone
# we need to adjust
#
# We also need to adjust if we do not
# have variable-length units
if not in_same_tz or total_days == 0:
offset1 = d1.utcoffset()
offset2 = d2.utcoffset()
if offset1:
d1 = d1 - offset1
if offset2:
d2 = d2 - offset2
hour_diff = d2.hour - d1.hour
min_diff = d2.minute - d1.minute
sec_diff = d2.second - d1.second
mic_diff = d2.microsecond - d1.microsecond
else:
hour_diff = d2.hour
min_diff = d2.minute
sec_diff = d2.second
mic_diff = d2.microsecond
if mic_diff < 0:
mic_diff += 1000000
sec_diff -= 1
if sec_diff < 0:
sec_diff += 60
min_diff -= 1
if min_diff < 0:
min_diff += 60
hour_diff -= 1
if hour_diff < 0:
hour_diff += 24
d_diff -= 1
y_diff = d2.year - d1.year
m_diff = d2.month - d1.month
d_diff += d2.day - d1.day
if d_diff < 0:
year = d2.year
month = d2.month
if month == 1:
month = 12
year -= 1
else:
month -= 1
leap = int(is_leap(year))
days_in_last_month = DAYS_PER_MONTHS[leap][month]
days_in_month = DAYS_PER_MONTHS[int(is_leap(d2.year))][d2.month]
if d_diff < days_in_month - days_in_last_month:
# We don't have a full month, we calculate days
if days_in_last_month < d1.day:
d_diff += d1.day
else:
d_diff += days_in_last_month
elif d_diff == days_in_month - days_in_last_month:
# We have exactly a full month
# We remove the days difference
# and add one to the months difference
d_diff = 0
m_diff += 1
else:
# We have a full month
d_diff += days_in_last_month
m_diff -= 1
if m_diff < 0:
m_diff += 12
y_diff -= 1
return PreciseDiff(
sign * y_diff,
sign * m_diff,
sign * d_diff,
sign * hour_diff,
sign * min_diff,
sign * sec_diff,
sign * mic_diff,
sign * total_days,
)
def _day_number(year, month, day): # type: (int, int, int) -> int
month = (month + 9) % 12
year = year - month // 10
return (
365 * year
+ year // 4
- year // 100
+ year // 400
+ (month * 306 + 5) // 10
+ (day - 1)
)

View File

@ -0,0 +1,107 @@
# The day constants
SUNDAY = 0
MONDAY = 1
TUESDAY = 2
WEDNESDAY = 3
THURSDAY = 4
FRIDAY = 5
SATURDAY = 6
# Number of X in Y.
YEARS_PER_CENTURY = 100
YEARS_PER_DECADE = 10
MONTHS_PER_YEAR = 12
WEEKS_PER_YEAR = 52
DAYS_PER_WEEK = 7
HOURS_PER_DAY = 24
MINUTES_PER_HOUR = 60
SECONDS_PER_MINUTE = 60
SECONDS_PER_HOUR = MINUTES_PER_HOUR * SECONDS_PER_MINUTE
SECONDS_PER_DAY = HOURS_PER_DAY * SECONDS_PER_HOUR
US_PER_SECOND = 1000000
# Formats
ATOM = "YYYY-MM-DDTHH:mm:ssZ"
COOKIE = "dddd, DD-MMM-YYYY HH:mm:ss zz"
ISO8601 = "YYYY-MM-DDTHH:mm:ssZ"
ISO8601_EXTENDED = "YYYY-MM-DDTHH:mm:ss.SSSSSSZ"
RFC822 = "ddd, DD MMM YY HH:mm:ss ZZ"
RFC850 = "dddd, DD-MMM-YY HH:mm:ss zz"
RFC1036 = "ddd, DD MMM YY HH:mm:ss ZZ"
RFC1123 = "ddd, DD MMM YYYY HH:mm:ss ZZ"
RFC2822 = "ddd, DD MMM YYYY HH:mm:ss ZZ"
RFC3339 = ISO8601
RFC3339_EXTENDED = ISO8601_EXTENDED
RSS = "ddd, DD MMM YYYY HH:mm:ss ZZ"
W3C = ISO8601
EPOCH_YEAR = 1970
DAYS_PER_N_YEAR = 365
DAYS_PER_L_YEAR = 366
USECS_PER_SEC = 1000000
SECS_PER_MIN = 60
SECS_PER_HOUR = 60 * SECS_PER_MIN
SECS_PER_DAY = SECS_PER_HOUR * 24
# 400-year chunks always have 146097 days (20871 weeks).
SECS_PER_400_YEARS = 146097 * SECS_PER_DAY
# The number of seconds in an aligned 100-year chunk, for those that
# do not begin with a leap year and those that do respectively.
SECS_PER_100_YEARS = (
(76 * DAYS_PER_N_YEAR + 24 * DAYS_PER_L_YEAR) * SECS_PER_DAY,
(75 * DAYS_PER_N_YEAR + 25 * DAYS_PER_L_YEAR) * SECS_PER_DAY,
)
# The number of seconds in an aligned 4-year chunk, for those that
# do not begin with a leap year and those that do respectively.
SECS_PER_4_YEARS = (
(4 * DAYS_PER_N_YEAR + 0 * DAYS_PER_L_YEAR) * SECS_PER_DAY,
(3 * DAYS_PER_N_YEAR + 1 * DAYS_PER_L_YEAR) * SECS_PER_DAY,
)
# The number of seconds in non-leap and leap years respectively.
SECS_PER_YEAR = (DAYS_PER_N_YEAR * SECS_PER_DAY, DAYS_PER_L_YEAR * SECS_PER_DAY)
DAYS_PER_YEAR = (DAYS_PER_N_YEAR, DAYS_PER_L_YEAR)
# The month lengths in non-leap and leap years respectively.
DAYS_PER_MONTHS = (
(-1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31),
(-1, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31),
)
# The day offsets of the beginning of each (1-based) month in non-leap
# and leap years respectively.
# For example, in a leap year there are 335 days before December.
MONTHS_OFFSETS = (
(-1, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365),
(-1, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366),
)
DAY_OF_WEEK_TABLE = (0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4)
TM_SUNDAY = 0
TM_MONDAY = 1
TM_TUESDAY = 2
TM_WEDNESDAY = 3
TM_THURSDAY = 4
TM_FRIDAY = 5
TM_SATURDAY = 6
TM_JANUARY = 0
TM_FEBRUARY = 1
TM_MARCH = 2
TM_APRIL = 3
TM_MAY = 4
TM_JUNE = 5
TM_JULY = 6
TM_AUGUST = 7
TM_SEPTEMBER = 8
TM_OCTOBER = 9
TM_NOVEMBER = 10
TM_DECEMBER = 11

View File

@ -0,0 +1,891 @@
from __future__ import absolute_import
from __future__ import division
import calendar
import math
from datetime import date
from datetime import timedelta
import pendulum
from .constants import FRIDAY
from .constants import MONDAY
from .constants import MONTHS_PER_YEAR
from .constants import SATURDAY
from .constants import SUNDAY
from .constants import THURSDAY
from .constants import TUESDAY
from .constants import WEDNESDAY
from .constants import YEARS_PER_CENTURY
from .constants import YEARS_PER_DECADE
from .exceptions import PendulumException
from .helpers import add_duration
from .mixins.default import FormattableMixin
from .period import Period
class Date(FormattableMixin, date):
# Names of days of the week
_days = {
SUNDAY: "Sunday",
MONDAY: "Monday",
TUESDAY: "Tuesday",
WEDNESDAY: "Wednesday",
THURSDAY: "Thursday",
FRIDAY: "Friday",
SATURDAY: "Saturday",
}
_MODIFIERS_VALID_UNITS = ["day", "week", "month", "year", "decade", "century"]
# Getters/Setters
def set(self, year=None, month=None, day=None):
return self.replace(year=year, month=month, day=day)
@property
def day_of_week(self):
"""
Returns the day of the week (0-6).
:rtype: int
"""
return self.isoweekday() % 7
@property
def day_of_year(self):
"""
Returns the day of the year (1-366).
:rtype: int
"""
k = 1 if self.is_leap_year() else 2
return (275 * self.month) // 9 - k * ((self.month + 9) // 12) + self.day - 30
@property
def week_of_year(self):
return self.isocalendar()[1]
@property
def days_in_month(self):
return calendar.monthrange(self.year, self.month)[1]
@property
def week_of_month(self):
first_day_of_month = self.replace(day=1)
return self.week_of_year - first_day_of_month.week_of_year + 1
@property
def age(self):
return self.diff(abs=False).in_years()
@property
def quarter(self):
return int(math.ceil(self.month / 3))
# String Formatting
def to_date_string(self):
"""
Format the instance as date.
:rtype: str
"""
return self.strftime("%Y-%m-%d")
def to_formatted_date_string(self):
"""
Format the instance as a readable date.
:rtype: str
"""
return self.strftime("%b %d, %Y")
def __repr__(self):
return (
"{klass}("
"{year}, {month}, {day}"
")".format(
klass=self.__class__.__name__,
year=self.year,
month=self.month,
day=self.day,
)
)
# COMPARISONS
def closest(self, dt1, dt2):
"""
Get the closest date from the instance.
:type dt1: Date or date
:type dt2: Date or date
:rtype: Date
"""
dt1 = self.__class__(dt1.year, dt1.month, dt1.day)
dt2 = self.__class__(dt2.year, dt2.month, dt2.day)
if self.diff(dt1).in_seconds() < self.diff(dt2).in_seconds():
return dt1
return dt2
def farthest(self, dt1, dt2):
"""
Get the farthest date from the instance.
:type dt1: Date or date
:type dt2: Date or date
:rtype: Date
"""
dt1 = self.__class__(dt1.year, dt1.month, dt1.day)
dt2 = self.__class__(dt2.year, dt2.month, dt2.day)
if self.diff(dt1).in_seconds() > self.diff(dt2).in_seconds():
return dt1
return dt2
def is_future(self):
"""
Determines if the instance is in the future, ie. greater than now.
:rtype: bool
"""
return self > self.today()
def is_past(self):
"""
Determines if the instance is in the past, ie. less than now.
:rtype: bool
"""
return self < self.today()
def is_leap_year(self):
"""
Determines if the instance is a leap year.
:rtype: bool
"""
return calendar.isleap(self.year)
def is_long_year(self):
"""
Determines if the instance is a long year
See link `<https://en.wikipedia.org/wiki/ISO_8601#Week_dates>`_
:rtype: bool
"""
return Date(self.year, 12, 28).isocalendar()[1] == 53
def is_same_day(self, dt):
"""
Checks if the passed in date is the same day as the instance current day.
:type dt: Date or date
:rtype: bool
"""
return self == dt
def is_anniversary(self, dt=None):
"""
Check if its the anniversary.
Compares the date/month values of the two dates.
:rtype: bool
"""
if dt is None:
dt = Date.today()
instance = self.__class__(dt.year, dt.month, dt.day)
return (self.month, self.day) == (instance.month, instance.day)
# the additional method for checking if today is the anniversary day
# the alias is provided to start using a new name and keep the backward compatibility
# the old name can be completely replaced with the new in one of the future versions
is_birthday = is_anniversary
# ADDITIONS AND SUBSTRACTIONS
def add(self, years=0, months=0, weeks=0, days=0):
"""
Add duration to the instance.
:param years: The number of years
:type years: int
:param months: The number of months
:type months: int
:param weeks: The number of weeks
:type weeks: int
:param days: The number of days
:type days: int
:rtype: Date
"""
dt = add_duration(
date(self.year, self.month, self.day),
years=years,
months=months,
weeks=weeks,
days=days,
)
return self.__class__(dt.year, dt.month, dt.day)
def subtract(self, years=0, months=0, weeks=0, days=0):
"""
Remove duration from the instance.
:param years: The number of years
:type years: int
:param months: The number of months
:type months: int
:param weeks: The number of weeks
:type weeks: int
:param days: The number of days
:type days: int
:rtype: Date
"""
return self.add(years=-years, months=-months, weeks=-weeks, days=-days)
def _add_timedelta(self, delta):
"""
Add timedelta duration to the instance.
:param delta: The timedelta instance
:type delta: pendulum.Duration or datetime.timedelta
:rtype: Date
"""
if isinstance(delta, pendulum.Duration):
return self.add(
years=delta.years,
months=delta.months,
weeks=delta.weeks,
days=delta.remaining_days,
)
return self.add(days=delta.days)
def _subtract_timedelta(self, delta):
"""
Remove timedelta duration from the instance.
:param delta: The timedelta instance
:type delta: pendulum.Duration or datetime.timedelta
:rtype: Date
"""
if isinstance(delta, pendulum.Duration):
return self.subtract(
years=delta.years,
months=delta.months,
weeks=delta.weeks,
days=delta.remaining_days,
)
return self.subtract(days=delta.days)
def __add__(self, other):
if not isinstance(other, timedelta):
return NotImplemented
return self._add_timedelta(other)
def __sub__(self, other):
if isinstance(other, timedelta):
return self._subtract_timedelta(other)
if not isinstance(other, date):
return NotImplemented
dt = self.__class__(other.year, other.month, other.day)
return dt.diff(self, False)
# DIFFERENCES
def diff(self, dt=None, abs=True):
"""
Returns the difference between two Date objects as a Period.
:type dt: Date or None
:param abs: Whether to return an absolute interval or not
:type abs: bool
:rtype: Period
"""
if dt is None:
dt = self.today()
return Period(self, Date(dt.year, dt.month, dt.day), absolute=abs)
def diff_for_humans(self, other=None, absolute=False, locale=None):
"""
Get the difference in a human readable format in the current locale.
When comparing a value in the past to default now:
1 day ago
5 months ago
When comparing a value in the future to default now:
1 day from now
5 months from now
When comparing a value in the past to another value:
1 day before
5 months before
When comparing a value in the future to another value:
1 day after
5 months after
:type other: Date
:param absolute: removes time difference modifiers ago, after, etc
:type absolute: bool
:param locale: The locale to use for localization
:type locale: str
:rtype: str
"""
is_now = other is None
if is_now:
other = self.today()
diff = self.diff(other)
return pendulum.format_diff(diff, is_now, absolute, locale)
# MODIFIERS
def start_of(self, unit):
"""
Returns a copy of the instance with the time reset
with the following rules:
* day: time to 00:00:00
* week: date to first day of the week and time to 00:00:00
* month: date to first day of the month and time to 00:00:00
* year: date to first day of the year and time to 00:00:00
* decade: date to first day of the decade and time to 00:00:00
* century: date to first day of century and time to 00:00:00
:param unit: The unit to reset to
:type unit: str
:rtype: Date
"""
if unit not in self._MODIFIERS_VALID_UNITS:
raise ValueError('Invalid unit "{}" for start_of()'.format(unit))
return getattr(self, "_start_of_{}".format(unit))()
def end_of(self, unit):
"""
Returns a copy of the instance with the time reset
with the following rules:
* week: date to last day of the week
* month: date to last day of the month
* year: date to last day of the year
* decade: date to last day of the decade
* century: date to last day of century
:param unit: The unit to reset to
:type unit: str
:rtype: Date
"""
if unit not in self._MODIFIERS_VALID_UNITS:
raise ValueError('Invalid unit "%s" for end_of()' % unit)
return getattr(self, "_end_of_%s" % unit)()
def _start_of_day(self):
"""
Compatibility method.
:rtype: Date
"""
return self
def _end_of_day(self):
"""
Compatibility method
:rtype: Date
"""
return self
def _start_of_month(self):
"""
Reset the date to the first day of the month.
:rtype: Date
"""
return self.set(self.year, self.month, 1)
def _end_of_month(self):
"""
Reset the date to the last day of the month.
:rtype: Date
"""
return self.set(self.year, self.month, self.days_in_month)
def _start_of_year(self):
"""
Reset the date to the first day of the year.
:rtype: Date
"""
return self.set(self.year, 1, 1)
def _end_of_year(self):
"""
Reset the date to the last day of the year.
:rtype: Date
"""
return self.set(self.year, 12, 31)
def _start_of_decade(self):
"""
Reset the date to the first day of the decade.
:rtype: Date
"""
year = self.year - self.year % YEARS_PER_DECADE
return self.set(year, 1, 1)
def _end_of_decade(self):
"""
Reset the date to the last day of the decade.
:rtype: Date
"""
year = self.year - self.year % YEARS_PER_DECADE + YEARS_PER_DECADE - 1
return self.set(year, 12, 31)
def _start_of_century(self):
"""
Reset the date to the first day of the century.
:rtype: Date
"""
year = self.year - 1 - (self.year - 1) % YEARS_PER_CENTURY + 1
return self.set(year, 1, 1)
def _end_of_century(self):
"""
Reset the date to the last day of the century.
:rtype: Date
"""
year = self.year - 1 - (self.year - 1) % YEARS_PER_CENTURY + YEARS_PER_CENTURY
return self.set(year, 12, 31)
def _start_of_week(self):
"""
Reset the date to the first day of the week.
:rtype: Date
"""
dt = self
if self.day_of_week != pendulum._WEEK_STARTS_AT:
dt = self.previous(pendulum._WEEK_STARTS_AT)
return dt.start_of("day")
def _end_of_week(self):
"""
Reset the date to the last day of the week.
:rtype: Date
"""
dt = self
if self.day_of_week != pendulum._WEEK_ENDS_AT:
dt = self.next(pendulum._WEEK_ENDS_AT)
return dt.end_of("day")
def next(self, day_of_week=None):
"""
Modify to the next occurrence of a given day of the week.
If no day_of_week is provided, modify to the next occurrence
of the current day of the week. Use the supplied consts
to indicate the desired day_of_week, ex. pendulum.MONDAY.
:param day_of_week: The next day of week to reset to.
:type day_of_week: int or None
:rtype: Date
"""
if day_of_week is None:
day_of_week = self.day_of_week
if day_of_week < SUNDAY or day_of_week > SATURDAY:
raise ValueError("Invalid day of week")
dt = self.add(days=1)
while dt.day_of_week != day_of_week:
dt = dt.add(days=1)
return dt
def previous(self, day_of_week=None):
"""
Modify to the previous occurrence of a given day of the week.
If no day_of_week is provided, modify to the previous occurrence
of the current day of the week. Use the supplied consts
to indicate the desired day_of_week, ex. pendulum.MONDAY.
:param day_of_week: The previous day of week to reset to.
:type day_of_week: int or None
:rtype: Date
"""
if day_of_week is None:
day_of_week = self.day_of_week
if day_of_week < SUNDAY or day_of_week > SATURDAY:
raise ValueError("Invalid day of week")
dt = self.subtract(days=1)
while dt.day_of_week != day_of_week:
dt = dt.subtract(days=1)
return dt
def first_of(self, unit, day_of_week=None):
"""
Returns an instance set to the first occurrence
of a given day of the week in the current unit.
If no day_of_week is provided, modify to the first day of the unit.
Use the supplied consts to indicate the desired day_of_week, ex. pendulum.MONDAY.
Supported units are month, quarter and year.
:param unit: The unit to use
:type unit: str
:type day_of_week: int or None
:rtype: Date
"""
if unit not in ["month", "quarter", "year"]:
raise ValueError('Invalid unit "{}" for first_of()'.format(unit))
return getattr(self, "_first_of_{}".format(unit))(day_of_week)
def last_of(self, unit, day_of_week=None):
"""
Returns an instance set to the last occurrence
of a given day of the week in the current unit.
If no day_of_week is provided, modify to the last day of the unit.
Use the supplied consts to indicate the desired day_of_week, ex. pendulum.MONDAY.
Supported units are month, quarter and year.
:param unit: The unit to use
:type unit: str
:type day_of_week: int or None
:rtype: Date
"""
if unit not in ["month", "quarter", "year"]:
raise ValueError('Invalid unit "{}" for first_of()'.format(unit))
return getattr(self, "_last_of_{}".format(unit))(day_of_week)
def nth_of(self, unit, nth, day_of_week):
"""
Returns a new instance set to the given occurrence
of a given day of the week in the current unit.
If the calculated occurrence is outside the scope of the current unit,
then raise an error. Use the supplied consts
to indicate the desired day_of_week, ex. pendulum.MONDAY.
Supported units are month, quarter and year.
:param unit: The unit to use
:type unit: str
:type nth: int
:type day_of_week: int or None
:rtype: Date
"""
if unit not in ["month", "quarter", "year"]:
raise ValueError('Invalid unit "{}" for first_of()'.format(unit))
dt = getattr(self, "_nth_of_{}".format(unit))(nth, day_of_week)
if dt is False:
raise PendulumException(
"Unable to find occurence {} of {} in {}".format(
nth, self._days[day_of_week], unit
)
)
return dt
def _first_of_month(self, day_of_week):
"""
Modify to the first occurrence of a given day of the week
in the current month. If no day_of_week is provided,
modify to the first day of the month. Use the supplied consts
to indicate the desired day_of_week, ex. pendulum.MONDAY.
:type day_of_week: int
:rtype: Date
"""
dt = self
if day_of_week is None:
return dt.set(day=1)
month = calendar.monthcalendar(dt.year, dt.month)
calendar_day = (day_of_week - 1) % 7
if month[0][calendar_day] > 0:
day_of_month = month[0][calendar_day]
else:
day_of_month = month[1][calendar_day]
return dt.set(day=day_of_month)
def _last_of_month(self, day_of_week=None):
"""
Modify to the last occurrence of a given day of the week
in the current month. If no day_of_week is provided,
modify to the last day of the month. Use the supplied consts
to indicate the desired day_of_week, ex. pendulum.MONDAY.
:type day_of_week: int or None
:rtype: Date
"""
dt = self
if day_of_week is None:
return dt.set(day=self.days_in_month)
month = calendar.monthcalendar(dt.year, dt.month)
calendar_day = (day_of_week - 1) % 7
if month[-1][calendar_day] > 0:
day_of_month = month[-1][calendar_day]
else:
day_of_month = month[-2][calendar_day]
return dt.set(day=day_of_month)
def _nth_of_month(self, nth, day_of_week):
"""
Modify to the given occurrence of a given day of the week
in the current month. If the calculated occurrence is outside,
the scope of the current month, then return False and no
modifications are made. Use the supplied consts
to indicate the desired day_of_week, ex. pendulum.MONDAY.
:type nth: int
:type day_of_week: int or None
:rtype: Date
"""
if nth == 1:
return self.first_of("month", day_of_week)
dt = self.first_of("month")
check = dt.format("YYYY-MM")
for i in range(nth - (1 if dt.day_of_week == day_of_week else 0)):
dt = dt.next(day_of_week)
if dt.format("YYYY-MM") == check:
return self.set(day=dt.day)
return False
def _first_of_quarter(self, day_of_week=None):
"""
Modify to the first occurrence of a given day of the week
in the current quarter. If no day_of_week is provided,
modify to the first day of the quarter. Use the supplied consts
to indicate the desired day_of_week, ex. pendulum.MONDAY.
:type day_of_week: int or None
:rtype: Date
"""
return self.set(self.year, self.quarter * 3 - 2, 1).first_of(
"month", day_of_week
)
def _last_of_quarter(self, day_of_week=None):
"""
Modify to the last occurrence of a given day of the week
in the current quarter. If no day_of_week is provided,
modify to the last day of the quarter. Use the supplied consts
to indicate the desired day_of_week, ex. pendulum.MONDAY.
:type day_of_week: int or None
:rtype: Date
"""
return self.set(self.year, self.quarter * 3, 1).last_of("month", day_of_week)
def _nth_of_quarter(self, nth, day_of_week):
"""
Modify to the given occurrence of a given day of the week
in the current quarter. If the calculated occurrence is outside,
the scope of the current quarter, then return False and no
modifications are made. Use the supplied consts
to indicate the desired day_of_week, ex. pendulum.MONDAY.
:type nth: int
:type day_of_week: int or None
:rtype: Date
"""
if nth == 1:
return self.first_of("quarter", day_of_week)
dt = self.replace(self.year, self.quarter * 3, 1)
last_month = dt.month
year = dt.year
dt = dt.first_of("quarter")
for i in range(nth - (1 if dt.day_of_week == day_of_week else 0)):
dt = dt.next(day_of_week)
if last_month < dt.month or year != dt.year:
return False
return self.set(self.year, dt.month, dt.day)
def _first_of_year(self, day_of_week=None):
"""
Modify to the first occurrence of a given day of the week
in the current year. If no day_of_week is provided,
modify to the first day of the year. Use the supplied consts
to indicate the desired day_of_week, ex. pendulum.MONDAY.
:type day_of_week: int or None
:rtype: Date
"""
return self.set(month=1).first_of("month", day_of_week)
def _last_of_year(self, day_of_week=None):
"""
Modify to the last occurrence of a given day of the week
in the current year. If no day_of_week is provided,
modify to the last day of the year. Use the supplied consts
to indicate the desired day_of_week, ex. pendulum.MONDAY.
:type day_of_week: int or None
:rtype: Date
"""
return self.set(month=MONTHS_PER_YEAR).last_of("month", day_of_week)
def _nth_of_year(self, nth, day_of_week):
"""
Modify to the given occurrence of a given day of the week
in the current year. If the calculated occurrence is outside,
the scope of the current year, then return False and no
modifications are made. Use the supplied consts
to indicate the desired day_of_week, ex. pendulum.MONDAY.
:type nth: int
:type day_of_week: int or None
:rtype: Date
"""
if nth == 1:
return self.first_of("year", day_of_week)
dt = self.first_of("year")
year = dt.year
for i in range(nth - (1 if dt.day_of_week == day_of_week else 0)):
dt = dt.next(day_of_week)
if year != dt.year:
return False
return self.set(self.year, dt.month, dt.day)
def average(self, dt=None):
"""
Modify the current instance to the average
of a given instance (default now) and the current instance.
:type dt: Date or date
:rtype: Date
"""
if dt is None:
dt = Date.today()
return self.add(days=int(self.diff(dt, False).in_days() / 2))
# Native methods override
@classmethod
def today(cls):
return pendulum.today().date()
@classmethod
def fromtimestamp(cls, t):
dt = super(Date, cls).fromtimestamp(t)
return cls(dt.year, dt.month, dt.day)
@classmethod
def fromordinal(cls, n):
dt = super(Date, cls).fromordinal(n)
return cls(dt.year, dt.month, dt.day)
def replace(self, year=None, month=None, day=None):
year = year if year is not None else self.year
month = month if month is not None else self.month
day = day if day is not None else self.day
return self.__class__(year, month, day)

View File

@ -0,0 +1,479 @@
from __future__ import absolute_import
from __future__ import division
from datetime import timedelta
import pendulum
from pendulum.utils._compat import PYPY
from pendulum.utils._compat import decode
from .constants import SECONDS_PER_DAY
from .constants import SECONDS_PER_HOUR
from .constants import SECONDS_PER_MINUTE
from .constants import US_PER_SECOND
def _divide_and_round(a, b):
"""divide a by b and round result to the nearest integer
When the ratio is exactly half-way between two integers,
the even integer is returned.
"""
# Based on the reference implementation for divmod_near
# in Objects/longobject.c.
q, r = divmod(a, b)
# round up if either r / b > 0.5, or r / b == 0.5 and q is odd.
# The expression r / b > 0.5 is equivalent to 2 * r > b if b is
# positive, 2 * r < b if b negative.
r *= 2
greater_than_half = r > b if b > 0 else r < b
if greater_than_half or r == b and q % 2 == 1:
q += 1
return q
class Duration(timedelta):
"""
Replacement for the standard timedelta class.
Provides several improvements over the base class.
"""
_y = None
_m = None
_w = None
_d = None
_h = None
_i = None
_s = None
_invert = None
def __new__(
cls,
days=0,
seconds=0,
microseconds=0,
milliseconds=0,
minutes=0,
hours=0,
weeks=0,
years=0,
months=0,
):
if not isinstance(years, int) or not isinstance(months, int):
raise ValueError("Float year and months are not supported")
self = timedelta.__new__(
cls,
days + years * 365 + months * 30,
seconds,
microseconds,
milliseconds,
minutes,
hours,
weeks,
)
# Intuitive normalization
total = self.total_seconds() - (years * 365 + months * 30) * SECONDS_PER_DAY
self._total = total
m = 1
if total < 0:
m = -1
self._microseconds = round(total % m * 1e6)
self._seconds = abs(int(total)) % SECONDS_PER_DAY * m
_days = abs(int(total)) // SECONDS_PER_DAY * m
self._days = _days
self._remaining_days = abs(_days) % 7 * m
self._weeks = abs(_days) // 7 * m
self._months = months
self._years = years
return self
def total_minutes(self):
return self.total_seconds() / SECONDS_PER_MINUTE
def total_hours(self):
return self.total_seconds() / SECONDS_PER_HOUR
def total_days(self):
return self.total_seconds() / SECONDS_PER_DAY
def total_weeks(self):
return self.total_days() / 7
if PYPY:
def total_seconds(self):
days = 0
if hasattr(self, "_years"):
days += self._years * 365
if hasattr(self, "_months"):
days += self._months * 30
if hasattr(self, "_remaining_days"):
days += self._weeks * 7 + self._remaining_days
else:
days += self._days
return (
(days * SECONDS_PER_DAY + self._seconds) * US_PER_SECOND
+ self._microseconds
) / US_PER_SECOND
@property
def years(self):
return self._years
@property
def months(self):
return self._months
@property
def weeks(self):
return self._weeks
if PYPY:
@property
def days(self):
return self._years * 365 + self._months * 30 + self._days
@property
def remaining_days(self):
return self._remaining_days
@property
def hours(self):
if self._h is None:
seconds = self._seconds
self._h = 0
if abs(seconds) >= 3600:
self._h = (abs(seconds) // 3600 % 24) * self._sign(seconds)
return self._h
@property
def minutes(self):
if self._i is None:
seconds = self._seconds
self._i = 0
if abs(seconds) >= 60:
self._i = (abs(seconds) // 60 % 60) * self._sign(seconds)
return self._i
@property
def seconds(self):
return self._seconds
@property
def remaining_seconds(self):
if self._s is None:
self._s = self._seconds
self._s = abs(self._s) % 60 * self._sign(self._s)
return self._s
@property
def microseconds(self):
return self._microseconds
@property
def invert(self):
if self._invert is None:
self._invert = self.total_seconds() < 0
return self._invert
def in_weeks(self):
return int(self.total_weeks())
def in_days(self):
return int(self.total_days())
def in_hours(self):
return int(self.total_hours())
def in_minutes(self):
return int(self.total_minutes())
def in_seconds(self):
return int(self.total_seconds())
def in_words(self, locale=None, separator=" "):
"""
Get the current interval in words in the current locale.
Ex: 6 jours 23 heures 58 minutes
:param locale: The locale to use. Defaults to current locale.
:type locale: str
:param separator: The separator to use between each unit
:type separator: str
:rtype: str
"""
periods = [
("year", self.years),
("month", self.months),
("week", self.weeks),
("day", self.remaining_days),
("hour", self.hours),
("minute", self.minutes),
("second", self.remaining_seconds),
]
if locale is None:
locale = pendulum.get_locale()
locale = pendulum.locale(locale)
parts = []
for period in periods:
unit, count = period
if abs(count) > 0:
translation = locale.translation(
"units.{}.{}".format(unit, locale.plural(abs(count)))
)
parts.append(translation.format(count))
if not parts:
if abs(self.microseconds) > 0:
unit = "units.second.{}".format(locale.plural(1))
count = "{:.2f}".format(abs(self.microseconds) / 1e6)
else:
unit = "units.microsecond.{}".format(locale.plural(0))
count = 0
translation = locale.translation(unit)
parts.append(translation.format(count))
return decode(separator.join(parts))
def _sign(self, value):
if value < 0:
return -1
return 1
def as_timedelta(self):
"""
Return the interval as a native timedelta.
:rtype: timedelta
"""
return timedelta(seconds=self.total_seconds())
def __str__(self):
return self.in_words()
def __repr__(self):
rep = "{}(".format(self.__class__.__name__)
if self._years:
rep += "years={}, ".format(self._years)
if self._months:
rep += "months={}, ".format(self._months)
if self._weeks:
rep += "weeks={}, ".format(self._weeks)
if self._days:
rep += "days={}, ".format(self._remaining_days)
if self.hours:
rep += "hours={}, ".format(self.hours)
if self.minutes:
rep += "minutes={}, ".format(self.minutes)
if self.remaining_seconds:
rep += "seconds={}, ".format(self.remaining_seconds)
if self.microseconds:
rep += "microseconds={}, ".format(self.microseconds)
rep += ")"
return rep.replace(", )", ")")
def __add__(self, other):
if isinstance(other, timedelta):
return self.__class__(seconds=self.total_seconds() + other.total_seconds())
return NotImplemented
__radd__ = __add__
def __sub__(self, other):
if isinstance(other, timedelta):
return self.__class__(seconds=self.total_seconds() - other.total_seconds())
return NotImplemented
def __neg__(self):
return self.__class__(
years=-self._years,
months=-self._months,
weeks=-self._weeks,
days=-self._remaining_days,
seconds=-self._seconds,
microseconds=-self._microseconds,
)
def _to_microseconds(self):
return (self._days * (24 * 3600) + self._seconds) * 1000000 + self._microseconds
def __mul__(self, other):
if isinstance(other, int):
return self.__class__(
years=self._years * other,
months=self._months * other,
seconds=self._total * other,
)
if isinstance(other, float):
usec = self._to_microseconds()
a, b = other.as_integer_ratio()
return self.__class__(0, 0, _divide_and_round(usec * a, b))
return NotImplemented
__rmul__ = __mul__
def __floordiv__(self, other):
if not isinstance(other, (int, timedelta)):
return NotImplemented
usec = self._to_microseconds()
if isinstance(other, timedelta):
return usec // other._to_microseconds()
if isinstance(other, int):
return self.__class__(
0,
0,
usec // other,
years=self._years // other,
months=self._months // other,
)
def __truediv__(self, other):
if not isinstance(other, (int, float, timedelta)):
return NotImplemented
usec = self._to_microseconds()
if isinstance(other, timedelta):
return usec / other._to_microseconds()
if isinstance(other, int):
return self.__class__(
0,
0,
_divide_and_round(usec, other),
years=_divide_and_round(self._years, other),
months=_divide_and_round(self._months, other),
)
if isinstance(other, float):
a, b = other.as_integer_ratio()
return self.__class__(
0,
0,
_divide_and_round(b * usec, a),
years=_divide_and_round(self._years * b, a),
months=_divide_and_round(self._months, other),
)
__div__ = __floordiv__
def __mod__(self, other):
if isinstance(other, timedelta):
r = self._to_microseconds() % other._to_microseconds()
return self.__class__(0, 0, r)
return NotImplemented
def __divmod__(self, other):
if isinstance(other, timedelta):
q, r = divmod(self._to_microseconds(), other._to_microseconds())
return q, self.__class__(0, 0, r)
return NotImplemented
Duration.min = Duration(days=-999999999)
Duration.max = Duration(
days=999999999, hours=23, minutes=59, seconds=59, microseconds=999999
)
Duration.resolution = Duration(microseconds=1)
class AbsoluteDuration(Duration):
"""
Duration that expresses a time difference in absolute values.
"""
def __new__(
cls,
days=0,
seconds=0,
microseconds=0,
milliseconds=0,
minutes=0,
hours=0,
weeks=0,
years=0,
months=0,
):
if not isinstance(years, int) or not isinstance(months, int):
raise ValueError("Float year and months are not supported")
self = timedelta.__new__(
cls, days, seconds, microseconds, milliseconds, minutes, hours, weeks
)
# We need to compute the total_seconds() value
# on a native timedelta object
delta = timedelta(
days, seconds, microseconds, milliseconds, minutes, hours, weeks
)
# Intuitive normalization
self._total = delta.total_seconds()
total = abs(self._total)
self._microseconds = round(total % 1 * 1e6)
self._seconds = int(total) % SECONDS_PER_DAY
days = int(total) // SECONDS_PER_DAY
self._days = abs(days + years * 365 + months * 30)
self._remaining_days = days % 7
self._weeks = days // 7
self._months = abs(months)
self._years = abs(years)
return self
def total_seconds(self):
return abs(self._total)
@property
def invert(self):
if self._invert is None:
self._invert = self._total < 0
return self._invert

View File

@ -0,0 +1,6 @@
from .parsing.exceptions import ParserError # noqa
class PendulumException(Exception):
pass

View File

@ -0,0 +1,4 @@
from .formatter import Formatter
__all__ = ["Formatter"]

View File

@ -0,0 +1,153 @@
import typing
import pendulum
from pendulum.utils._compat import decode
from ..locales.locale import Locale
class DifferenceFormatter(object):
"""
Handles formatting differences in text.
"""
def __init__(self, locale="en"):
self._locale = Locale.load(locale)
def format(
self, diff, is_now=True, absolute=False, locale=None
): # type: (pendulum.Period, bool, bool, typing.Optional[str]) -> str
"""
Formats a difference.
:param diff: The difference to format
:type diff: pendulum.period.Period
:param is_now: Whether the difference includes now
:type is_now: bool
:param absolute: Whether it's an absolute difference or not
:type absolute: bool
:param locale: The locale to use
:type locale: str or None
:rtype: str
"""
if locale is None:
locale = self._locale
else:
locale = Locale.load(locale)
count = diff.remaining_seconds
if diff.years > 0:
unit = "year"
count = diff.years
if diff.months > 6:
count += 1
elif diff.months == 11 and (diff.weeks * 7 + diff.remaining_days) > 15:
unit = "year"
count = 1
elif diff.months > 0:
unit = "month"
count = diff.months
if (diff.weeks * 7 + diff.remaining_days) >= 27:
count += 1
elif diff.weeks > 0:
unit = "week"
count = diff.weeks
if diff.remaining_days > 3:
count += 1
elif diff.remaining_days > 0:
unit = "day"
count = diff.remaining_days
if diff.hours >= 22:
count += 1
elif diff.hours > 0:
unit = "hour"
count = diff.hours
elif diff.minutes > 0:
unit = "minute"
count = diff.minutes
elif 10 < diff.remaining_seconds <= 59:
unit = "second"
count = diff.remaining_seconds
else:
# We check if the "a few seconds" unit exists
time = locale.get("custom.units.few_second")
if time is not None:
if absolute:
return time
key = "custom"
is_future = diff.invert
if is_now:
if is_future:
key += ".from_now"
else:
key += ".ago"
else:
if is_future:
key += ".after"
else:
key += ".before"
return locale.get(key).format(time)
else:
unit = "second"
count = diff.remaining_seconds
if count == 0:
count = 1
if absolute:
key = "translations.units.{}".format(unit)
else:
is_future = diff.invert
if is_now:
# Relative to now, so we can use
# the CLDR data
key = "translations.relative.{}".format(unit)
if is_future:
key += ".future"
else:
key += ".past"
else:
# Absolute comparison
# So we have to use the custom locale data
# Checking for special pluralization rules
key = "custom.units_relative"
if is_future:
key += ".{}.future".format(unit)
else:
key += ".{}.past".format(unit)
trans = locale.get(key)
if not trans:
# No special rule
time = locale.get(
"translations.units.{}.{}".format(unit, locale.plural(count))
).format(count)
else:
time = trans[locale.plural(count)].format(count)
key = "custom"
if is_future:
key += ".after"
else:
key += ".before"
return locale.get(key).format(decode(time))
key += ".{}".format(locale.plural(count))
return decode(locale.get(key).format(count))

View File

@ -0,0 +1,685 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import datetime
import re
import typing
import pendulum
from pendulum.locales.locale import Locale
from pendulum.utils._compat import decode
_MATCH_1 = r"\d"
_MATCH_2 = r"\d\d"
_MATCH_3 = r"\d{3}"
_MATCH_4 = r"\d{4}"
_MATCH_6 = r"[+-]?\d{6}"
_MATCH_1_TO_2 = r"\d\d?"
_MATCH_1_TO_2_LEFT_PAD = r"[0-9 ]\d?"
_MATCH_1_TO_3 = r"\d{1,3}"
_MATCH_1_TO_4 = r"\d{1,4}"
_MATCH_1_TO_6 = r"[+-]?\d{1,6}"
_MATCH_3_TO_4 = r"\d{3}\d?"
_MATCH_5_TO_6 = r"\d{5}\d?"
_MATCH_UNSIGNED = r"\d+"
_MATCH_SIGNED = r"[+-]?\d+"
_MATCH_OFFSET = r"[Zz]|[+-]\d\d:?\d\d"
_MATCH_SHORT_OFFSET = r"[Zz]|[+-]\d\d(?::?\d\d)?"
_MATCH_TIMESTAMP = r"[+-]?\d+(\.\d{1,6})?"
_MATCH_WORD = (
"(?i)[0-9]*"
"['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+"
r"|[\u0600-\u06FF/]+(\s*?[\u0600-\u06FF]+){1,2}"
)
_MATCH_TIMEZONE = "[A-Za-z0-9-+]+(/[A-Za-z0-9-+_]+)?"
class Formatter:
_TOKENS = (
r"\[([^\[]*)\]|\\(.)|"
"("
"Mo|MM?M?M?"
"|Do|DDDo|DD?D?D?|ddd?d?|do?"
"|E{1,4}"
"|w[o|w]?|W[o|W]?|Qo?"
"|YYYY|YY|Y"
"|gg(ggg?)?|GG(GGG?)?"
"|a|A"
"|hh?|HH?|kk?"
"|mm?|ss?|S{1,9}"
"|x|X"
"|zz?|ZZ?"
"|LTS|LT|LL?L?L?"
")"
)
_FORMAT_RE = re.compile(_TOKENS)
_FROM_FORMAT_RE = re.compile(r"(?<!\\\[)" + _TOKENS + r"(?!\\\])")
_LOCALIZABLE_TOKENS = {
"Qo": None,
"MMMM": "months.wide",
"MMM": "months.abbreviated",
"Mo": None,
"DDDo": None,
"Do": lambda locale: tuple(
r"\d+{}".format(o) for o in locale.get("custom.ordinal").values()
),
"dddd": "days.wide",
"ddd": "days.abbreviated",
"dd": "days.short",
"do": None,
"Wo": None,
"wo": None,
"A": lambda locale: (
locale.translation("day_periods.am"),
locale.translation("day_periods.pm"),
),
"a": lambda locale: (
locale.translation("day_periods.am").lower(),
locale.translation("day_periods.pm").lower(),
),
}
_TOKENS_RULES = {
# Year
"YYYY": lambda dt: "{:d}".format(dt.year),
"YY": lambda dt: "{:d}".format(dt.year)[2:],
"Y": lambda dt: "{:d}".format(dt.year),
# Quarter
"Q": lambda dt: "{:d}".format(dt.quarter),
# Month
"MM": lambda dt: "{:02d}".format(dt.month),
"M": lambda dt: "{:d}".format(dt.month),
# Day
"DD": lambda dt: "{:02d}".format(dt.day),
"D": lambda dt: "{:d}".format(dt.day),
# Day of Year
"DDDD": lambda dt: "{:03d}".format(dt.day_of_year),
"DDD": lambda dt: "{:d}".format(dt.day_of_year),
# Day of Week
"d": lambda dt: "{:d}".format(dt.day_of_week),
# Day of ISO Week
"E": lambda dt: "{:d}".format(dt.isoweekday()),
# Hour
"HH": lambda dt: "{:02d}".format(dt.hour),
"H": lambda dt: "{:d}".format(dt.hour),
"hh": lambda dt: "{:02d}".format(dt.hour % 12 or 12),
"h": lambda dt: "{:d}".format(dt.hour % 12 or 12),
# Minute
"mm": lambda dt: "{:02d}".format(dt.minute),
"m": lambda dt: "{:d}".format(dt.minute),
# Second
"ss": lambda dt: "{:02d}".format(dt.second),
"s": lambda dt: "{:d}".format(dt.second),
# Fractional second
"S": lambda dt: "{:01d}".format(dt.microsecond // 100000),
"SS": lambda dt: "{:02d}".format(dt.microsecond // 10000),
"SSS": lambda dt: "{:03d}".format(dt.microsecond // 1000),
"SSSS": lambda dt: "{:04d}".format(dt.microsecond // 100),
"SSSSS": lambda dt: "{:05d}".format(dt.microsecond // 10),
"SSSSSS": lambda dt: "{:06d}".format(dt.microsecond),
# Timestamp
"X": lambda dt: "{:d}".format(dt.int_timestamp),
"x": lambda dt: "{:d}".format(dt.int_timestamp * 1000 + dt.microsecond // 1000),
# Timezone
"zz": lambda dt: "{}".format(dt.tzname() if dt.tzinfo is not None else ""),
"z": lambda dt: "{}".format(dt.timezone_name or ""),
}
_DATE_FORMATS = {
"LTS": "formats.time.full",
"LT": "formats.time.short",
"L": "formats.date.short",
"LL": "formats.date.long",
"LLL": "formats.datetime.long",
"LLLL": "formats.datetime.full",
}
_DEFAULT_DATE_FORMATS = {
"LTS": "h:mm:ss A",
"LT": "h:mm A",
"L": "MM/DD/YYYY",
"LL": "MMMM D, YYYY",
"LLL": "MMMM D, YYYY h:mm A",
"LLLL": "dddd, MMMM D, YYYY h:mm A",
}
_REGEX_TOKENS = {
"Y": _MATCH_SIGNED,
"YY": (_MATCH_1_TO_2, _MATCH_2),
"YYYY": (_MATCH_1_TO_4, _MATCH_4),
"Q": _MATCH_1,
"Qo": None,
"M": _MATCH_1_TO_2,
"MM": (_MATCH_1_TO_2, _MATCH_2),
"MMM": _MATCH_WORD,
"MMMM": _MATCH_WORD,
"D": _MATCH_1_TO_2,
"DD": (_MATCH_1_TO_2_LEFT_PAD, _MATCH_2),
"DDD": _MATCH_1_TO_3,
"DDDD": _MATCH_3,
"dddd": _MATCH_WORD,
"ddd": _MATCH_WORD,
"dd": _MATCH_WORD,
"d": _MATCH_1,
"E": _MATCH_1,
"Do": None,
"H": _MATCH_1_TO_2,
"HH": (_MATCH_1_TO_2, _MATCH_2),
"h": _MATCH_1_TO_2,
"hh": (_MATCH_1_TO_2, _MATCH_2),
"m": _MATCH_1_TO_2,
"mm": (_MATCH_1_TO_2, _MATCH_2),
"s": _MATCH_1_TO_2,
"ss": (_MATCH_1_TO_2, _MATCH_2),
"S": (_MATCH_1_TO_3, _MATCH_1),
"SS": (_MATCH_1_TO_3, _MATCH_2),
"SSS": (_MATCH_1_TO_3, _MATCH_3),
"SSSS": _MATCH_UNSIGNED,
"SSSSS": _MATCH_UNSIGNED,
"SSSSSS": _MATCH_UNSIGNED,
"x": _MATCH_SIGNED,
"X": _MATCH_TIMESTAMP,
"ZZ": _MATCH_SHORT_OFFSET,
"Z": _MATCH_OFFSET,
"z": _MATCH_TIMEZONE,
}
_PARSE_TOKENS = {
"YYYY": lambda year: int(year),
"YY": lambda year: int(year),
"Q": lambda quarter: int(quarter),
"MMMM": lambda month: month,
"MMM": lambda month: month,
"MM": lambda month: int(month),
"M": lambda month: int(month),
"DDDD": lambda day: int(day),
"DDD": lambda day: int(day),
"DD": lambda day: int(day),
"D": lambda day: int(day),
"dddd": lambda weekday: weekday,
"ddd": lambda weekday: weekday,
"dd": lambda weekday: weekday,
"d": lambda weekday: int(weekday) % 7,
"E": lambda weekday: int(weekday),
"HH": lambda hour: int(hour),
"H": lambda hour: int(hour),
"hh": lambda hour: int(hour),
"h": lambda hour: int(hour),
"mm": lambda minute: int(minute),
"m": lambda minute: int(minute),
"ss": lambda second: int(second),
"s": lambda second: int(second),
"S": lambda us: int(us) * 100000,
"SS": lambda us: int(us) * 10000,
"SSS": lambda us: int(us) * 1000,
"SSSS": lambda us: int(us) * 100,
"SSSSS": lambda us: int(us) * 10,
"SSSSSS": lambda us: int(us),
"a": lambda meridiem: meridiem,
"X": lambda ts: float(ts),
"x": lambda ts: float(ts) / 1e3,
"ZZ": str,
"Z": str,
"z": str,
}
def format(
self, dt, fmt, locale=None
): # type: (pendulum.DateTime, str, typing.Optional[typing.Union[str, Locale]]) -> str
"""
Formats a DateTime instance with a given format and locale.
:param dt: The instance to format
:type dt: pendulum.DateTime
:param fmt: The format to use
:type fmt: str
:param locale: The locale to use
:type locale: str or Locale or None
:rtype: str
"""
if not locale:
locale = pendulum.get_locale()
locale = Locale.load(locale)
result = self._FORMAT_RE.sub(
lambda m: m.group(1)
if m.group(1)
else m.group(2)
if m.group(2)
else self._format_token(dt, m.group(3), locale),
fmt,
)
return decode(result)
def _format_token(
self, dt, token, locale
): # type: (pendulum.DateTime, str, Locale) -> str
"""
Formats a DateTime instance with a given token and locale.
:param dt: The instance to format
:type dt: pendulum.DateTime
:param token: The token to use
:type token: str
:param locale: The locale to use
:type locale: Locale
:rtype: str
"""
if token in self._DATE_FORMATS:
fmt = locale.get("custom.date_formats.{}".format(token))
if fmt is None:
fmt = self._DEFAULT_DATE_FORMATS[token]
return self.format(dt, fmt, locale)
if token in self._LOCALIZABLE_TOKENS:
return self._format_localizable_token(dt, token, locale)
if token in self._TOKENS_RULES:
return self._TOKENS_RULES[token](dt)
# Timezone
if token in ["ZZ", "Z"]:
if dt.tzinfo is None:
return ""
separator = ":" if token == "Z" else ""
offset = dt.utcoffset() or datetime.timedelta()
minutes = offset.total_seconds() / 60
if minutes >= 0:
sign = "+"
else:
sign = "-"
hour, minute = divmod(abs(int(minutes)), 60)
return "{}{:02d}{}{:02d}".format(sign, hour, separator, minute)
def _format_localizable_token(
self, dt, token, locale
): # type: (pendulum.DateTime, str, Locale) -> str
"""
Formats a DateTime instance
with a given localizable token and locale.
:param dt: The instance to format
:type dt: pendulum.DateTime
:param token: The token to use
:type token: str
:param locale: The locale to use
:type locale: Locale
:rtype: str
"""
if token == "MMM":
return locale.get("translations.months.abbreviated")[dt.month]
elif token == "MMMM":
return locale.get("translations.months.wide")[dt.month]
elif token == "dd":
return locale.get("translations.days.short")[dt.day_of_week]
elif token == "ddd":
return locale.get("translations.days.abbreviated")[dt.day_of_week]
elif token == "dddd":
return locale.get("translations.days.wide")[dt.day_of_week]
elif token == "Do":
return locale.ordinalize(dt.day)
elif token == "do":
return locale.ordinalize(dt.day_of_week)
elif token == "Mo":
return locale.ordinalize(dt.month)
elif token == "Qo":
return locale.ordinalize(dt.quarter)
elif token == "wo":
return locale.ordinalize(dt.week_of_year)
elif token == "DDDo":
return locale.ordinalize(dt.day_of_year)
elif token == "A":
key = "translations.day_periods"
if dt.hour >= 12:
key += ".pm"
else:
key += ".am"
return locale.get(key)
else:
return token
def parse(
self,
time, # type: str
fmt, # type: str
now, # type: pendulum.DateTime
locale=None, # type: typing.Optional[str]
): # type: (...) -> typing.Dict[str, typing.Any]
"""
Parses a time string matching a given format as a tuple.
:param time: The timestring
:param fmt: The format
:param now: The datetime to use as "now"
:param locale: The locale to use
:return: The parsed elements
"""
escaped_fmt = re.escape(fmt)
tokens = self._FROM_FORMAT_RE.findall(escaped_fmt)
if not tokens:
return time
if not locale:
locale = pendulum.get_locale()
locale = Locale.load(locale)
parsed = {
"year": None,
"month": None,
"day": None,
"hour": None,
"minute": None,
"second": None,
"microsecond": None,
"tz": None,
"quarter": None,
"day_of_week": None,
"day_of_year": None,
"meridiem": None,
"timestamp": None,
}
pattern = self._FROM_FORMAT_RE.sub(
lambda m: self._replace_tokens(m.group(0), locale), escaped_fmt
)
if not re.search("^" + pattern + "$", time):
raise ValueError("String does not match format {}".format(fmt))
re.sub(pattern, lambda m: self._get_parsed_values(m, parsed, locale, now), time)
return self._check_parsed(parsed, now)
def _check_parsed(
self, parsed, now
): # type: (typing.Dict[str, typing.Any], pendulum.DateTime) -> typing.Dict[str, typing.Any]
"""
Checks validity of parsed elements.
:param parsed: The elements to parse.
:return: The validated elements.
"""
validated = {
"year": parsed["year"],
"month": parsed["month"],
"day": parsed["day"],
"hour": parsed["hour"],
"minute": parsed["minute"],
"second": parsed["second"],
"microsecond": parsed["microsecond"],
"tz": None,
}
# If timestamp has been specified
# we use it and don't go any further
if parsed["timestamp"] is not None:
str_us = str(parsed["timestamp"])
if "." in str_us:
microseconds = int("{}".format(str_us.split(".")[1].ljust(6, "0")))
else:
microseconds = 0
from pendulum.helpers import local_time
time = local_time(parsed["timestamp"], 0, microseconds)
validated["year"] = time[0]
validated["month"] = time[1]
validated["day"] = time[2]
validated["hour"] = time[3]
validated["minute"] = time[4]
validated["second"] = time[5]
validated["microsecond"] = time[6]
return validated
if parsed["quarter"] is not None:
if validated["year"] is not None:
dt = pendulum.datetime(validated["year"], 1, 1)
else:
dt = now
dt = dt.start_of("year")
while dt.quarter != parsed["quarter"]:
dt = dt.add(months=3)
validated["year"] = dt.year
validated["month"] = dt.month
validated["day"] = dt.day
if validated["year"] is None:
validated["year"] = now.year
if parsed["day_of_year"] is not None:
dt = pendulum.parse(
"{}-{:>03d}".format(validated["year"], parsed["day_of_year"])
)
validated["month"] = dt.month
validated["day"] = dt.day
if parsed["day_of_week"] is not None:
dt = pendulum.datetime(
validated["year"],
validated["month"] or now.month,
validated["day"] or now.day,
)
dt = dt.start_of("week").subtract(days=1)
dt = dt.next(parsed["day_of_week"])
validated["year"] = dt.year
validated["month"] = dt.month
validated["day"] = dt.day
# Meridiem
if parsed["meridiem"] is not None:
# If the time is greater than 13:00:00
# This is not valid
if validated["hour"] is None:
raise ValueError("Invalid Date")
t = (
validated["hour"],
validated["minute"],
validated["second"],
validated["microsecond"],
)
if t >= (13, 0, 0, 0):
raise ValueError("Invalid date")
pm = parsed["meridiem"] == "pm"
validated["hour"] %= 12
if pm:
validated["hour"] += 12
if validated["month"] is None:
if parsed["year"] is not None:
validated["month"] = parsed["month"] or 1
else:
validated["month"] = parsed["month"] or now.month
if validated["day"] is None:
if parsed["year"] is not None or parsed["month"] is not None:
validated["day"] = parsed["day"] or 1
else:
validated["day"] = parsed["day"] or now.day
for part in ["hour", "minute", "second", "microsecond"]:
if validated[part] is None:
validated[part] = 0
validated["tz"] = parsed["tz"]
return validated
def _get_parsed_values(
self, m, parsed, locale, now
): # type: (typing.Match[str], typing.Dict[str, typing.Any], Locale, pendulum.DateTime) -> None
for token, index in m.re.groupindex.items():
if token in self._LOCALIZABLE_TOKENS:
self._get_parsed_locale_value(token, m.group(index), parsed, locale)
else:
self._get_parsed_value(token, m.group(index), parsed, now)
def _get_parsed_value(
self, token, value, parsed, now
): # type: (str, str, typing.Dict[str, typing.Any], pendulum.DateTime) -> None
parsed_token = self._PARSE_TOKENS[token](value)
if "Y" in token:
if token == "YY":
parsed_token = now.year // 100 * 100 + parsed_token
parsed["year"] = parsed_token
elif "Q" == token:
parsed["quarter"] = parsed_token
elif token in ["MM", "M"]:
parsed["month"] = parsed_token
elif token in ["DDDD", "DDD"]:
parsed["day_of_year"] = parsed_token
elif "D" in token:
parsed["day"] = parsed_token
elif "H" in token:
parsed["hour"] = parsed_token
elif token in ["hh", "h"]:
if parsed_token > 12:
raise ValueError("Invalid date")
parsed["hour"] = parsed_token
elif "m" in token:
parsed["minute"] = parsed_token
elif "s" in token:
parsed["second"] = parsed_token
elif "S" in token:
parsed["microsecond"] = parsed_token
elif token in ["d", "E"]:
parsed["day_of_week"] = parsed_token
elif token in ["X", "x"]:
parsed["timestamp"] = parsed_token
elif token in ["ZZ", "Z"]:
negative = True if value.startswith("-") else False
tz = value[1:]
if ":" not in tz:
if len(tz) == 2:
tz = "{}00".format(tz)
off_hour = tz[0:2]
off_minute = tz[2:4]
else:
off_hour, off_minute = tz.split(":")
offset = ((int(off_hour) * 60) + int(off_minute)) * 60
if negative:
offset = -1 * offset
parsed["tz"] = pendulum.timezone(offset)
elif token == "z":
# Full timezone
if value not in pendulum.timezones:
raise ValueError("Invalid date")
parsed["tz"] = pendulum.timezone(value)
def _get_parsed_locale_value(
self, token, value, parsed, locale
): # type: (str, str, typing.Dict[str, typing.Any], Locale) -> None
if token == "MMMM":
unit = "month"
match = "months.wide"
elif token == "MMM":
unit = "month"
match = "months.abbreviated"
elif token == "Do":
parsed["day"] = int(re.match(r"(\d+)", value).group(1))
return
elif token == "dddd":
unit = "day_of_week"
match = "days.wide"
elif token == "ddd":
unit = "day_of_week"
match = "days.abbreviated"
elif token == "dd":
unit = "day_of_week"
match = "days.short"
elif token in ["a", "A"]:
valid_values = [
locale.translation("day_periods.am"),
locale.translation("day_periods.pm"),
]
if token == "a":
value = value.lower()
valid_values = list(map(lambda x: x.lower(), valid_values))
if value not in valid_values:
raise ValueError("Invalid date")
parsed["meridiem"] = ["am", "pm"][valid_values.index(value)]
return
else:
raise ValueError('Invalid token "{}"'.format(token))
parsed[unit] = locale.match_translation(match, value)
if value is None:
raise ValueError("Invalid date")
def _replace_tokens(self, token, locale): # type: (str, Locale) -> str
if token.startswith("[") and token.endswith("]"):
return token[1:-1]
elif token.startswith("\\"):
if len(token) == 2 and token[1] in {"[", "]"}:
return ""
return token
elif token not in self._REGEX_TOKENS and token not in self._LOCALIZABLE_TOKENS:
raise ValueError("Unsupported token: {}".format(token))
if token in self._LOCALIZABLE_TOKENS:
values = self._LOCALIZABLE_TOKENS[token]
if callable(values):
candidates = values(locale)
else:
candidates = tuple(
locale.translation(self._LOCALIZABLE_TOKENS[token]).values()
)
else:
candidates = self._REGEX_TOKENS[token]
if not candidates:
raise ValueError("Unsupported token: {}".format(token))
if not isinstance(candidates, tuple):
candidates = (candidates,)
pattern = "(?P<{}>{})".format(token, "|".join([decode(p) for p in candidates]))
return pattern

View File

@ -0,0 +1,224 @@
from __future__ import absolute_import
import os
import struct
from contextlib import contextmanager
from datetime import date
from datetime import datetime
from datetime import timedelta
from math import copysign
from typing import TYPE_CHECKING
from typing import Iterator
from typing import Optional
from typing import TypeVar
from typing import overload
import pendulum
from .constants import DAYS_PER_MONTHS
from .formatting.difference_formatter import DifferenceFormatter
from .locales.locale import Locale
if TYPE_CHECKING:
# Prevent import cycles
from .period import Period
with_extensions = os.getenv("PENDULUM_EXTENSIONS", "1") == "1"
_DT = TypeVar("_DT", bound=datetime)
_D = TypeVar("_D", bound=date)
try:
if not with_extensions or struct.calcsize("P") == 4:
raise ImportError()
from ._extensions._helpers import local_time
from ._extensions._helpers import precise_diff
from ._extensions._helpers import is_leap
from ._extensions._helpers import is_long_year
from ._extensions._helpers import week_day
from ._extensions._helpers import days_in_year
from ._extensions._helpers import timestamp
except ImportError:
from ._extensions.helpers import local_time # noqa
from ._extensions.helpers import precise_diff # noqa
from ._extensions.helpers import is_leap # noqa
from ._extensions.helpers import is_long_year # noqa
from ._extensions.helpers import week_day # noqa
from ._extensions.helpers import days_in_year # noqa
from ._extensions.helpers import timestamp # noqa
difference_formatter = DifferenceFormatter()
@overload
def add_duration(
dt, # type: _DT
years=0, # type: int
months=0, # type: int
weeks=0, # type: int
days=0, # type: int
hours=0, # type: int
minutes=0, # type: int
seconds=0, # type: int
microseconds=0, # type: int
): # type: (...) -> _DT
pass
@overload
def add_duration(
dt, # type: _D
years=0, # type: int
months=0, # type: int
weeks=0, # type: int
days=0, # type: int
): # type: (...) -> _D
pass
def add_duration(
dt,
years=0,
months=0,
weeks=0,
days=0,
hours=0,
minutes=0,
seconds=0,
microseconds=0,
):
"""
Adds a duration to a date/datetime instance.
"""
days += weeks * 7
if (
isinstance(dt, date)
and not isinstance(dt, datetime)
and any([hours, minutes, seconds, microseconds])
):
raise RuntimeError("Time elements cannot be added to a date instance.")
# Normalizing
if abs(microseconds) > 999999:
s = _sign(microseconds)
div, mod = divmod(microseconds * s, 1000000)
microseconds = mod * s
seconds += div * s
if abs(seconds) > 59:
s = _sign(seconds)
div, mod = divmod(seconds * s, 60)
seconds = mod * s
minutes += div * s
if abs(minutes) > 59:
s = _sign(minutes)
div, mod = divmod(minutes * s, 60)
minutes = mod * s
hours += div * s
if abs(hours) > 23:
s = _sign(hours)
div, mod = divmod(hours * s, 24)
hours = mod * s
days += div * s
if abs(months) > 11:
s = _sign(months)
div, mod = divmod(months * s, 12)
months = mod * s
years += div * s
year = dt.year + years
month = dt.month
if months:
month += months
if month > 12:
year += 1
month -= 12
elif month < 1:
year -= 1
month += 12
day = min(DAYS_PER_MONTHS[int(is_leap(year))][month], dt.day)
dt = dt.replace(year=year, month=month, day=day)
return dt + timedelta(
days=days,
hours=hours,
minutes=minutes,
seconds=seconds,
microseconds=microseconds,
)
def format_diff(
diff, is_now=True, absolute=False, locale=None
): # type: (Period, bool, bool, Optional[str]) -> str
if locale is None:
locale = get_locale()
return difference_formatter.format(diff, is_now, absolute, locale)
def _sign(x):
return int(copysign(1, x))
# Global helpers
@contextmanager
def test(mock): # type: (pendulum.DateTime) -> Iterator[None]
set_test_now(mock)
try:
yield
finally:
set_test_now()
def set_test_now(test_now=None): # type: (Optional[pendulum.DateTime]) -> None
pendulum._TEST_NOW = test_now
def get_test_now(): # type: () -> Optional[pendulum.DateTime]
return pendulum._TEST_NOW
def has_test_now(): # type: () -> bool
return pendulum._TEST_NOW is not None
def locale(name): # type: (str) -> Locale
return Locale.load(name)
def set_locale(name): # type: (str) -> None
locale(name)
pendulum._LOCALE = name
def get_locale(): # type: () -> str
return pendulum._LOCALE
def week_starts_at(wday): # type: (int) -> None
if wday < pendulum.SUNDAY or wday > pendulum.SATURDAY:
raise ValueError("Invalid week day as start of week.")
pendulum._WEEK_STARTS_AT = wday
def week_ends_at(wday): # type: (int) -> None
if wday < pendulum.SUNDAY or wday > pendulum.SATURDAY:
raise ValueError("Invalid week day as start of week.")
pendulum._WEEK_ENDS_AT = wday

View File

@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
"""
da custom locale file.
"""
translations = {
# Relative time
"after": "{0} efter",
"before": "{0} før",
# Date formats
"date_formats": {
"LTS": "HH:mm:ss",
"LT": "HH:mm",
"LLLL": "dddd [d.] D. MMMM YYYY HH:mm",
"LLL": "D. MMMM YYYY HH:mm",
"LL": "D. MMMM YYYY",
"L": "DD/MM/YYYY",
},
}

View File

@ -0,0 +1,150 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from .custom import translations as custom_translations
"""
da locale file.
It has been generated automatically and must not be modified directly.
"""
locale = {
"plural": lambda n: "one"
if (
(n == n and ((n == 1)))
or ((not (0 == 0 and ((0 == 0)))) and (n == n and ((n == 0) or (n == 1))))
)
else "other",
"ordinal": lambda n: "other",
"translations": {
"days": {
"abbreviated": {
0: "søn.",
1: "man.",
2: "tir.",
3: "ons.",
4: "tor.",
5: "fre.",
6: "lør.",
},
"narrow": {0: "S", 1: "M", 2: "T", 3: "O", 4: "T", 5: "F", 6: "L"},
"short": {0: "", 1: "ma", 2: "ti", 3: "on", 4: "to", 5: "fr", 6: ""},
"wide": {
0: "søndag",
1: "mandag",
2: "tirsdag",
3: "onsdag",
4: "torsdag",
5: "fredag",
6: "lørdag",
},
},
"months": {
"abbreviated": {
1: "jan.",
2: "feb.",
3: "mar.",
4: "apr.",
5: "maj",
6: "jun.",
7: "jul.",
8: "aug.",
9: "sep.",
10: "okt.",
11: "nov.",
12: "dec.",
},
"narrow": {
1: "J",
2: "F",
3: "M",
4: "A",
5: "M",
6: "J",
7: "J",
8: "A",
9: "S",
10: "O",
11: "N",
12: "D",
},
"wide": {
1: "januar",
2: "februar",
3: "marts",
4: "april",
5: "maj",
6: "juni",
7: "juli",
8: "august",
9: "september",
10: "oktober",
11: "november",
12: "december",
},
},
"units": {
"year": {"one": "{0} år", "other": "{0} år"},
"month": {"one": "{0} måned", "other": "{0} måneder"},
"week": {"one": "{0} uge", "other": "{0} uger"},
"day": {"one": "{0} dag", "other": "{0} dage"},
"hour": {"one": "{0} time", "other": "{0} timer"},
"minute": {"one": "{0} minut", "other": "{0} minutter"},
"second": {"one": "{0} sekund", "other": "{0} sekunder"},
"microsecond": {"one": "{0} mikrosekund", "other": "{0} mikrosekunder"},
},
"relative": {
"year": {
"future": {"other": "om {0} år", "one": "om {0} år"},
"past": {"other": "for {0} år siden", "one": "for {0} år siden"},
},
"month": {
"future": {"other": "om {0} måneder", "one": "om {0} måned"},
"past": {
"other": "for {0} måneder siden",
"one": "for {0} måned siden",
},
},
"week": {
"future": {"other": "om {0} uger", "one": "om {0} uge"},
"past": {"other": "for {0} uger siden", "one": "for {0} uge siden"},
},
"day": {
"future": {"other": "om {0} dage", "one": "om {0} dag"},
"past": {"other": "for {0} dage siden", "one": "for {0} dag siden"},
},
"hour": {
"future": {"other": "om {0} timer", "one": "om {0} time"},
"past": {"other": "for {0} timer siden", "one": "for {0} time siden"},
},
"minute": {
"future": {"other": "om {0} minutter", "one": "om {0} minut"},
"past": {
"other": "for {0} minutter siden",
"one": "for {0} minut siden",
},
},
"second": {
"future": {"other": "om {0} sekunder", "one": "om {0} sekund"},
"past": {
"other": "for {0} sekunder siden",
"one": "for {0} sekund siden",
},
},
},
"day_periods": {
"midnight": "midnat",
"am": "AM",
"pm": "PM",
"morning1": "om morgenen",
"morning2": "om formiddagen",
"afternoon1": "om eftermiddagen",
"evening1": "om aftenen",
"night1": "om natten",
},
},
"custom": custom_translations,
}

View File

@ -0,0 +1,40 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
"""
de custom locale file.
"""
translations = {
# Relative time
"after": "{0} später",
"before": "{0} zuvor",
"units_relative": {
"year": {
"future": {"one": "{0} Jahr", "other": "{0} Jahren"},
"past": {"one": "{0} Jahr", "other": "{0} Jahren"},
},
"month": {
"future": {"one": "{0} Monat", "other": "{0} Monaten"},
"past": {"one": "{0} Monat", "other": "{0} Monaten"},
},
"week": {
"future": {"one": "{0} Woche", "other": "{0} Wochen"},
"past": {"one": "{0} Woche", "other": "{0} Wochen"},
},
"day": {
"future": {"one": "{0} Tag", "other": "{0} Tagen"},
"past": {"one": "{0} Tag", "other": "{0} Tagen"},
},
},
# Date formats
"date_formats": {
"LTS": "HH:mm:ss",
"LT": "HH:mm",
"LLLL": "dddd, D. MMMM YYYY HH:mm",
"LLL": "D. MMMM YYYY HH:mm",
"LL": "D. MMMM YYYY",
"L": "DD.MM.YYYY",
},
}

View File

@ -0,0 +1,147 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from .custom import translations as custom_translations
"""
de locale file.
It has been generated automatically and must not be modified directly.
"""
locale = {
"plural": lambda n: "one"
if ((n == n and ((n == 1))) and (0 == 0 and ((0 == 0))))
else "other",
"ordinal": lambda n: "other",
"translations": {
"days": {
"abbreviated": {
0: "So.",
1: "Mo.",
2: "Di.",
3: "Mi.",
4: "Do.",
5: "Fr.",
6: "Sa.",
},
"narrow": {0: "S", 1: "M", 2: "D", 3: "M", 4: "D", 5: "F", 6: "S"},
"short": {
0: "So.",
1: "Mo.",
2: "Di.",
3: "Mi.",
4: "Do.",
5: "Fr.",
6: "Sa.",
},
"wide": {
0: "Sonntag",
1: "Montag",
2: "Dienstag",
3: "Mittwoch",
4: "Donnerstag",
5: "Freitag",
6: "Samstag",
},
},
"months": {
"abbreviated": {
1: "Jan.",
2: "Feb.",
3: "März",
4: "Apr.",
5: "Mai",
6: "Juni",
7: "Juli",
8: "Aug.",
9: "Sep.",
10: "Okt.",
11: "Nov.",
12: "Dez.",
},
"narrow": {
1: "J",
2: "F",
3: "M",
4: "A",
5: "M",
6: "J",
7: "J",
8: "A",
9: "S",
10: "O",
11: "N",
12: "D",
},
"wide": {
1: "Januar",
2: "Februar",
3: "März",
4: "April",
5: "Mai",
6: "Juni",
7: "Juli",
8: "August",
9: "September",
10: "Oktober",
11: "November",
12: "Dezember",
},
},
"units": {
"year": {"one": "{0} Jahr", "other": "{0} Jahre"},
"month": {"one": "{0} Monat", "other": "{0} Monate"},
"week": {"one": "{0} Woche", "other": "{0} Wochen"},
"day": {"one": "{0} Tag", "other": "{0} Tage"},
"hour": {"one": "{0} Stunde", "other": "{0} Stunden"},
"minute": {"one": "{0} Minute", "other": "{0} Minuten"},
"second": {"one": "{0} Sekunde", "other": "{0} Sekunden"},
"microsecond": {"one": "{0} Mikrosekunde", "other": "{0} Mikrosekunden"},
},
"relative": {
"year": {
"future": {"other": "in {0} Jahren", "one": "in {0} Jahr"},
"past": {"other": "vor {0} Jahren", "one": "vor {0} Jahr"},
},
"month": {
"future": {"other": "in {0} Monaten", "one": "in {0} Monat"},
"past": {"other": "vor {0} Monaten", "one": "vor {0} Monat"},
},
"week": {
"future": {"other": "in {0} Wochen", "one": "in {0} Woche"},
"past": {"other": "vor {0} Wochen", "one": "vor {0} Woche"},
},
"day": {
"future": {"other": "in {0} Tagen", "one": "in {0} Tag"},
"past": {"other": "vor {0} Tagen", "one": "vor {0} Tag"},
},
"hour": {
"future": {"other": "in {0} Stunden", "one": "in {0} Stunde"},
"past": {"other": "vor {0} Stunden", "one": "vor {0} Stunde"},
},
"minute": {
"future": {"other": "in {0} Minuten", "one": "in {0} Minute"},
"past": {"other": "vor {0} Minuten", "one": "vor {0} Minute"},
},
"second": {
"future": {"other": "in {0} Sekunden", "one": "in {0} Sekunde"},
"past": {"other": "vor {0} Sekunden", "one": "vor {0} Sekunde"},
},
},
"day_periods": {
"midnight": "Mitternacht",
"am": "vorm.",
"pm": "nachm.",
"morning1": "morgens",
"morning2": "vormittags",
"afternoon1": "mittags",
"afternoon2": "nachmittags",
"evening1": "abends",
"night1": "nachts",
},
},
"custom": custom_translations,
}

View File

@ -0,0 +1,27 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
"""
en custom locale file.
"""
translations = {
"units": {"few_second": "a few seconds"},
# Relative time
"ago": "{} ago",
"from_now": "in {}",
"after": "{0} after",
"before": "{0} before",
# Ordinals
"ordinal": {"one": "st", "two": "nd", "few": "rd", "other": "th"},
# Date formats
"date_formats": {
"LTS": "h:mm:ss A",
"LT": "h:mm A",
"L": "MM/DD/YYYY",
"LL": "MMMM D, YYYY",
"LLL": "MMMM D, YYYY h:mm A",
"LLLL": "dddd, MMMM D, YYYY h:mm A",
},
}

View File

@ -0,0 +1,153 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from .custom import translations as custom_translations
"""
en locale file.
It has been generated automatically and must not be modified directly.
"""
locale = {
"plural": lambda n: "one"
if ((n == n and ((n == 1))) and (0 == 0 and ((0 == 0))))
else "other",
"ordinal": lambda n: "few"
if (
((n % 10) == (n % 10) and (((n % 10) == 3)))
and (not ((n % 100) == (n % 100) and (((n % 100) == 13))))
)
else "one"
if (
((n % 10) == (n % 10) and (((n % 10) == 1)))
and (not ((n % 100) == (n % 100) and (((n % 100) == 11))))
)
else "two"
if (
((n % 10) == (n % 10) and (((n % 10) == 2)))
and (not ((n % 100) == (n % 100) and (((n % 100) == 12))))
)
else "other",
"translations": {
"days": {
"abbreviated": {
0: "Sun",
1: "Mon",
2: "Tue",
3: "Wed",
4: "Thu",
5: "Fri",
6: "Sat",
},
"narrow": {0: "S", 1: "M", 2: "T", 3: "W", 4: "T", 5: "F", 6: "S"},
"short": {0: "Su", 1: "Mo", 2: "Tu", 3: "We", 4: "Th", 5: "Fr", 6: "Sa"},
"wide": {
0: "Sunday",
1: "Monday",
2: "Tuesday",
3: "Wednesday",
4: "Thursday",
5: "Friday",
6: "Saturday",
},
},
"months": {
"abbreviated": {
1: "Jan",
2: "Feb",
3: "Mar",
4: "Apr",
5: "May",
6: "Jun",
7: "Jul",
8: "Aug",
9: "Sep",
10: "Oct",
11: "Nov",
12: "Dec",
},
"narrow": {
1: "J",
2: "F",
3: "M",
4: "A",
5: "M",
6: "J",
7: "J",
8: "A",
9: "S",
10: "O",
11: "N",
12: "D",
},
"wide": {
1: "January",
2: "February",
3: "March",
4: "April",
5: "May",
6: "June",
7: "July",
8: "August",
9: "September",
10: "October",
11: "November",
12: "December",
},
},
"units": {
"year": {"one": "{0} year", "other": "{0} years"},
"month": {"one": "{0} month", "other": "{0} months"},
"week": {"one": "{0} week", "other": "{0} weeks"},
"day": {"one": "{0} day", "other": "{0} days"},
"hour": {"one": "{0} hour", "other": "{0} hours"},
"minute": {"one": "{0} minute", "other": "{0} minutes"},
"second": {"one": "{0} second", "other": "{0} seconds"},
"microsecond": {"one": "{0} microsecond", "other": "{0} microseconds"},
},
"relative": {
"year": {
"future": {"other": "in {0} years", "one": "in {0} year"},
"past": {"other": "{0} years ago", "one": "{0} year ago"},
},
"month": {
"future": {"other": "in {0} months", "one": "in {0} month"},
"past": {"other": "{0} months ago", "one": "{0} month ago"},
},
"week": {
"future": {"other": "in {0} weeks", "one": "in {0} week"},
"past": {"other": "{0} weeks ago", "one": "{0} week ago"},
},
"day": {
"future": {"other": "in {0} days", "one": "in {0} day"},
"past": {"other": "{0} days ago", "one": "{0} day ago"},
},
"hour": {
"future": {"other": "in {0} hours", "one": "in {0} hour"},
"past": {"other": "{0} hours ago", "one": "{0} hour ago"},
},
"minute": {
"future": {"other": "in {0} minutes", "one": "in {0} minute"},
"past": {"other": "{0} minutes ago", "one": "{0} minute ago"},
},
"second": {
"future": {"other": "in {0} seconds", "one": "in {0} second"},
"past": {"other": "{0} seconds ago", "one": "{0} second ago"},
},
},
"day_periods": {
"midnight": "midnight",
"am": "AM",
"noon": "noon",
"pm": "PM",
"morning1": "in the morning",
"afternoon1": "in the afternoon",
"evening1": "in the evening",
"night1": "at night",
},
},
"custom": custom_translations,
}

View File

@ -0,0 +1,27 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
"""
es custom locale file.
"""
translations = {
"units": {"few_second": "unos segundos"},
# Relative time
"ago": "hace {0}",
"from_now": "dentro de {0}",
"after": "{0} después",
"before": "{0} antes",
# Ordinals
"ordinal": {"other": "º"},
# Date formats
"date_formats": {
"LTS": "H:mm:ss",
"LT": "H:mm",
"LLLL": "dddd, D [de] MMMM [de] YYYY H:mm",
"LLL": "D [de] MMMM [de] YYYY H:mm",
"LL": "D [de] MMMM [de] YYYY",
"L": "DD/MM/YYYY",
},
}

View File

@ -0,0 +1,144 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from .custom import translations as custom_translations
"""
es locale file.
It has been generated automatically and must not be modified directly.
"""
locale = {
"plural": lambda n: "one" if (n == n and ((n == 1))) else "other",
"ordinal": lambda n: "other",
"translations": {
"days": {
"abbreviated": {
0: "dom.",
1: "lun.",
2: "mar.",
3: "mié.",
4: "jue.",
5: "vie.",
6: "sáb.",
},
"narrow": {0: "D", 1: "L", 2: "M", 3: "X", 4: "J", 5: "V", 6: "S"},
"short": {0: "DO", 1: "LU", 2: "MA", 3: "MI", 4: "JU", 5: "VI", 6: "SA"},
"wide": {
0: "domingo",
1: "lunes",
2: "martes",
3: "miércoles",
4: "jueves",
5: "viernes",
6: "sábado",
},
},
"months": {
"abbreviated": {
1: "ene.",
2: "feb.",
3: "mar.",
4: "abr.",
5: "may.",
6: "jun.",
7: "jul.",
8: "ago.",
9: "sept.",
10: "oct.",
11: "nov.",
12: "dic.",
},
"narrow": {
1: "E",
2: "F",
3: "M",
4: "A",
5: "M",
6: "J",
7: "J",
8: "A",
9: "S",
10: "O",
11: "N",
12: "D",
},
"wide": {
1: "enero",
2: "febrero",
3: "marzo",
4: "abril",
5: "mayo",
6: "junio",
7: "julio",
8: "agosto",
9: "septiembre",
10: "octubre",
11: "noviembre",
12: "diciembre",
},
},
"units": {
"year": {"one": "{0} año", "other": "{0} años"},
"month": {"one": "{0} mes", "other": "{0} meses"},
"week": {"one": "{0} semana", "other": "{0} semanas"},
"day": {"one": "{0} día", "other": "{0} días"},
"hour": {"one": "{0} hora", "other": "{0} horas"},
"minute": {"one": "{0} minuto", "other": "{0} minutos"},
"second": {"one": "{0} segundo", "other": "{0} segundos"},
"microsecond": {"one": "{0} microsegundo", "other": "{0} microsegundos"},
},
"relative": {
"year": {
"future": {"other": "dentro de {0} años", "one": "dentro de {0} año"},
"past": {"other": "hace {0} años", "one": "hace {0} año"},
},
"month": {
"future": {"other": "dentro de {0} meses", "one": "dentro de {0} mes"},
"past": {"other": "hace {0} meses", "one": "hace {0} mes"},
},
"week": {
"future": {
"other": "dentro de {0} semanas",
"one": "dentro de {0} semana",
},
"past": {"other": "hace {0} semanas", "one": "hace {0} semana"},
},
"day": {
"future": {"other": "dentro de {0} días", "one": "dentro de {0} día"},
"past": {"other": "hace {0} días", "one": "hace {0} día"},
},
"hour": {
"future": {"other": "dentro de {0} horas", "one": "dentro de {0} hora"},
"past": {"other": "hace {0} horas", "one": "hace {0} hora"},
},
"minute": {
"future": {
"other": "dentro de {0} minutos",
"one": "dentro de {0} minuto",
},
"past": {"other": "hace {0} minutos", "one": "hace {0} minuto"},
},
"second": {
"future": {
"other": "dentro de {0} segundos",
"one": "dentro de {0} segundo",
},
"past": {"other": "hace {0} segundos", "one": "hace {0} segundo"},
},
},
"day_periods": {
"am": "a. m.",
"noon": "del mediodía",
"pm": "p. m.",
"morning1": "de la madrugada",
"morning2": "de la mañana",
"evening1": "de la tarde",
"night1": "de la noche",
},
},
"custom": custom_translations,
}

View File

@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
"""
fa custom locale file.
"""
translations = {
# Relative time
"after": "{0} پس از",
"before": "{0} پیش از",
# Date formats
"date_formats": {
"LTS": "HH:mm:ss",
"LT": "HH:mm",
"LLLL": "dddd, D MMMM YYYY HH:mm",
"LLL": "D MMMM YYYY HH:mm",
"LL": "D MMMM YYYY",
"L": "DD/MM/YYYY",
},
}

View File

@ -0,0 +1,138 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from .custom import translations as custom_translations
"""
fa locale file.
It has been generated automatically and must not be modified directly.
"""
locale = {
"plural": lambda n: "one"
if ((n == n and ((n == 0))) or (n == n and ((n == 1))))
else "other",
"ordinal": lambda n: "other",
"translations": {
"days": {
"abbreviated": {
0: "یکشنبه",
1: "دوشنبه",
2: "سه\u200cشنبه",
3: "چهارشنبه",
4: "پنجشنبه",
5: "جمعه",
6: "شنبه",
},
"narrow": {0: "ی", 1: "د", 2: "س", 3: "چ", 4: "پ", 5: "ج", 6: "ش"},
"short": {0: "۱ش", 1: "۲ش", 2: "۳ش", 3: "۴ش", 4: "۵ش", 5: "ج", 6: "ش"},
"wide": {
0: "یکشنبه",
1: "دوشنبه",
2: "سه\u200cشنبه",
3: "چهارشنبه",
4: "پنجشنبه",
5: "جمعه",
6: "شنبه",
},
},
"months": {
"abbreviated": {
1: "ژانویهٔ",
2: "فوریهٔ",
3: "مارس",
4: "آوریل",
5: "مهٔ",
6: "ژوئن",
7: "ژوئیهٔ",
8: "اوت",
9: "سپتامبر",
10: "اکتبر",
11: "نوامبر",
12: "دسامبر",
},
"narrow": {
1: "ژ",
2: "ف",
3: "م",
4: "آ",
5: "م",
6: "ژ",
7: "ژ",
8: "ا",
9: "س",
10: "ا",
11: "ن",
12: "د",
},
"wide": {
1: "ژانویهٔ",
2: "فوریهٔ",
3: "مارس",
4: "آوریل",
5: "مهٔ",
6: "ژوئن",
7: "ژوئیهٔ",
8: "اوت",
9: "سپتامبر",
10: "اکتبر",
11: "نوامبر",
12: "دسامبر",
},
},
"units": {
"year": {"one": "{0} سال", "other": "{0} سال"},
"month": {"one": "{0} ماه", "other": "{0} ماه"},
"week": {"one": "{0} هفته", "other": "{0} هفته"},
"day": {"one": "{0} روز", "other": "{0} روز"},
"hour": {"one": "{0} ساعت", "other": "{0} ساعت"},
"minute": {"one": "{0} دقیقه", "other": "{0} دقیقه"},
"second": {"one": "{0} ثانیه", "other": "{0} ثانیه"},
"microsecond": {"one": "{0} میکروثانیه", "other": "{0} میکروثانیه"},
},
"relative": {
"year": {
"future": {"other": "{0} سال بعد", "one": "{0} سال بعد"},
"past": {"other": "{0} سال پیش", "one": "{0} سال پیش"},
},
"month": {
"future": {"other": "{0} ماه بعد", "one": "{0} ماه بعد"},
"past": {"other": "{0} ماه پیش", "one": "{0} ماه پیش"},
},
"week": {
"future": {"other": "{0} هفته بعد", "one": "{0} هفته بعد"},
"past": {"other": "{0} هفته پیش", "one": "{0} هفته پیش"},
},
"day": {
"future": {"other": "{0} روز بعد", "one": "{0} روز بعد"},
"past": {"other": "{0} روز پیش", "one": "{0} روز پیش"},
},
"hour": {
"future": {"other": "{0} ساعت بعد", "one": "{0} ساعت بعد"},
"past": {"other": "{0} ساعت پیش", "one": "{0} ساعت پیش"},
},
"minute": {
"future": {"other": "{0} دقیقه بعد", "one": "{0} دقیقه بعد"},
"past": {"other": "{0} دقیقه پیش", "one": "{0} دقیقه پیش"},
},
"second": {
"future": {"other": "{0} ثانیه بعد", "one": "{0} ثانیه بعد"},
"past": {"other": "{0} ثانیه پیش", "one": "{0} ثانیه پیش"},
},
},
"day_periods": {
"midnight": "نیمه\u200cشب",
"am": "قبل\u200cازظهر",
"noon": "ظهر",
"pm": "بعدازظهر",
"morning1": "صبح",
"afternoon1": "عصر",
"evening1": "عصر",
"night1": "شب",
},
},
"custom": custom_translations,
}

View File

@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
"""
fo custom locale file.
"""
translations = {
# Relative time
"after": "{0} aftaná",
"before": "{0} áðrenn",
# Ordinals
"ordinal": {"other": "."},
# Date formats
"date_formats": {
"LTS": "HH:mm:ss",
"LT": "HH:mm",
"LLLL": "dddd D. MMMM, YYYY HH:mm",
"LLL": "D MMMM YYYY HH:mm",
"LL": "D MMMM YYYY",
"L": "DD/MM/YYYY",
},
}

View File

@ -0,0 +1,135 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from .custom import translations as custom_translations
"""
fo locale file.
It has been generated automatically and must not be modified directly.
"""
locale = {
"plural": lambda n: "one" if (n == n and ((n == 1))) else "other",
"ordinal": lambda n: "other",
"translations": {
"days": {
"abbreviated": {
0: "sun.",
1: "mán.",
2: "týs.",
3: "mik.",
4: "hós.",
5: "frí.",
6: "ley.",
},
"narrow": {0: "S", 1: "M", 2: "T", 3: "M", 4: "H", 5: "F", 6: "L"},
"short": {
0: "su.",
1: "má.",
2: "tý.",
3: "mi.",
4: "hó.",
5: "fr.",
6: "le.",
},
"wide": {
0: "sunnudagur",
1: "mánadagur",
2: "týsdagur",
3: "mikudagur",
4: "hósdagur",
5: "fríggjadagur",
6: "leygardagur",
},
},
"months": {
"abbreviated": {
1: "jan.",
2: "feb.",
3: "mar.",
4: "apr.",
5: "mai",
6: "jun.",
7: "jul.",
8: "aug.",
9: "sep.",
10: "okt.",
11: "nov.",
12: "des.",
},
"narrow": {
1: "J",
2: "F",
3: "M",
4: "A",
5: "M",
6: "J",
7: "J",
8: "A",
9: "S",
10: "O",
11: "N",
12: "D",
},
"wide": {
1: "januar",
2: "februar",
3: "mars",
4: "apríl",
5: "mai",
6: "juni",
7: "juli",
8: "august",
9: "september",
10: "oktober",
11: "november",
12: "desember",
},
},
"units": {
"year": {"one": "{0} ár", "other": "{0} ár"},
"month": {"one": "{0} mánaður", "other": "{0} mánaðir"},
"week": {"one": "{0} vika", "other": "{0} vikur"},
"day": {"one": "{0} dagur", "other": "{0} dagar"},
"hour": {"one": "{0} tími", "other": "{0} tímar"},
"minute": {"one": "{0} minuttur", "other": "{0} minuttir"},
"second": {"one": "{0} sekund", "other": "{0} sekundir"},
"microsecond": {"one": "{0} mikrosekund", "other": "{0} mikrosekundir"},
},
"relative": {
"year": {
"future": {"other": "um {0} ár", "one": "um {0} ár"},
"past": {"other": "{0} ár síðan", "one": "{0} ár síðan"},
},
"month": {
"future": {"other": "um {0} mánaðir", "one": "um {0} mánað"},
"past": {"other": "{0} mánaðir síðan", "one": "{0} mánað síðan"},
},
"week": {
"future": {"other": "um {0} vikur", "one": "um {0} viku"},
"past": {"other": "{0} vikur síðan", "one": "{0} vika síðan"},
},
"day": {
"future": {"other": "um {0} dagar", "one": "um {0} dag"},
"past": {"other": "{0} dagar síðan", "one": "{0} dagur síðan"},
},
"hour": {
"future": {"other": "um {0} tímar", "one": "um {0} tíma"},
"past": {"other": "{0} tímar síðan", "one": "{0} tími síðan"},
},
"minute": {
"future": {"other": "um {0} minuttir", "one": "um {0} minutt"},
"past": {"other": "{0} minuttir síðan", "one": "{0} minutt síðan"},
},
"second": {
"future": {"other": "um {0} sekund", "one": "um {0} sekund"},
"past": {"other": "{0} sekund síðan", "one": "{0} sekund síðan"},
},
},
"day_periods": {"am": "AM", "pm": "PM"},
},
"custom": custom_translations,
}

View File

@ -0,0 +1,27 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
"""
fr custom locale file.
"""
translations = {
"units": {"few_second": "quelques secondes"},
# Relative Time
"ago": "il y a {0}",
"from_now": "dans {0}",
"after": "{0} après",
"before": "{0} avant",
# Ordinals
"ordinal": {"one": "er", "other": "e"},
# Date formats
"date_formats": {
"LTS": "HH:mm:ss",
"LT": "HH:mm",
"LLLL": "dddd D MMMM YYYY HH:mm",
"LLL": "D MMMM YYYY HH:mm",
"LL": "D MMMM YYYY",
"L": "DD/MM/YYYY",
},
}

View File

@ -0,0 +1,136 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from .custom import translations as custom_translations
"""
fr locale file.
It has been generated automatically and must not be modified directly.
"""
locale = {
"plural": lambda n: "one" if (n == n and ((n == 0) or (n == 1))) else "other",
"ordinal": lambda n: "one" if (n == n and ((n == 1))) else "other",
"translations": {
"days": {
"abbreviated": {
0: "dim.",
1: "lun.",
2: "mar.",
3: "mer.",
4: "jeu.",
5: "ven.",
6: "sam.",
},
"narrow": {0: "D", 1: "L", 2: "M", 3: "M", 4: "J", 5: "V", 6: "S"},
"short": {0: "di", 1: "lu", 2: "ma", 3: "me", 4: "je", 5: "ve", 6: "sa"},
"wide": {
0: "dimanche",
1: "lundi",
2: "mardi",
3: "mercredi",
4: "jeudi",
5: "vendredi",
6: "samedi",
},
},
"months": {
"abbreviated": {
1: "janv.",
2: "févr.",
3: "mars",
4: "avr.",
5: "mai",
6: "juin",
7: "juil.",
8: "août",
9: "sept.",
10: "oct.",
11: "nov.",
12: "déc.",
},
"narrow": {
1: "J",
2: "F",
3: "M",
4: "A",
5: "M",
6: "J",
7: "J",
8: "A",
9: "S",
10: "O",
11: "N",
12: "D",
},
"wide": {
1: "janvier",
2: "février",
3: "mars",
4: "avril",
5: "mai",
6: "juin",
7: "juillet",
8: "août",
9: "septembre",
10: "octobre",
11: "novembre",
12: "décembre",
},
},
"units": {
"year": {"one": "{0} an", "other": "{0} ans"},
"month": {"one": "{0} mois", "other": "{0} mois"},
"week": {"one": "{0} semaine", "other": "{0} semaines"},
"day": {"one": "{0} jour", "other": "{0} jours"},
"hour": {"one": "{0} heure", "other": "{0} heures"},
"minute": {"one": "{0} minute", "other": "{0} minutes"},
"second": {"one": "{0} seconde", "other": "{0} secondes"},
"microsecond": {"one": "{0} microseconde", "other": "{0} microsecondes"},
},
"relative": {
"year": {
"future": {"other": "dans {0} ans", "one": "dans {0} an"},
"past": {"other": "il y a {0} ans", "one": "il y a {0} an"},
},
"month": {
"future": {"other": "dans {0} mois", "one": "dans {0} mois"},
"past": {"other": "il y a {0} mois", "one": "il y a {0} mois"},
},
"week": {
"future": {"other": "dans {0} semaines", "one": "dans {0} semaine"},
"past": {"other": "il y a {0} semaines", "one": "il y a {0} semaine"},
},
"day": {
"future": {"other": "dans {0} jours", "one": "dans {0} jour"},
"past": {"other": "il y a {0} jours", "one": "il y a {0} jour"},
},
"hour": {
"future": {"other": "dans {0} heures", "one": "dans {0} heure"},
"past": {"other": "il y a {0} heures", "one": "il y a {0} heure"},
},
"minute": {
"future": {"other": "dans {0} minutes", "one": "dans {0} minute"},
"past": {"other": "il y a {0} minutes", "one": "il y a {0} minute"},
},
"second": {
"future": {"other": "dans {0} secondes", "one": "dans {0} seconde"},
"past": {"other": "il y a {0} secondes", "one": "il y a {0} seconde"},
},
},
"day_periods": {
"midnight": "minuit",
"am": "AM",
"noon": "midi",
"pm": "PM",
"morning1": "du matin",
"afternoon1": "de laprès-midi",
"evening1": "du soir",
"night1": "de nuit",
},
},
"custom": custom_translations,
}

Some files were not shown because too many files have changed in this diff Show More