Compare commits

...

68 Commits

Author SHA1 Message Date
Timo Furrer cc698817b4 release: 0.5.0 2018-05-26 15:19:57 +02:00
Timo Furrer 9964e9305e Merge pull request #154 from alxwrd/support-locales
Allow slang methods to change locale
2018-05-26 15:16:17 +02:00
Alex Ward d87236a30d update authors 2018-05-26 11:19:57 +01:00
Alex Ward d44a1dd2f6 add tracer tests for slang_ locales 2018-05-26 11:19:36 +01:00
Timo Furrer ed5d72b34c Merge pull request #155 from DavidMuller/patch-1
Add Example of Creating a MayaDT Using a Unix Time
2018-05-22 21:38:21 +02:00
David Muller 9c1e94f956 Add example that instantiates MayaDT w/ unix time 2018-05-22 10:45:01 -07:00
Alex Ward 6755968f69 remove unused calendar import 2018-05-22 09:57:32 +01:00
Alex Ward 3760af2c41 use pendulum and dateparser for slang_date 2018-05-21 22:37:31 +01:00
Alex Ward df09625e74 fix failing test 2018-05-15 22:56:28 +01:00
Alex Ward 2d848bd62a typo fix 2018-05-15 22:55:07 +01:00
Alex Ward a99cddf76f allow changing the locale for slang_time 2018-05-15 22:27:16 +01:00
Timo Furrer 1b432d6626 release: 0.4.3 2018-05-15 09:13:21 +02:00
Timo Furrer f09d6eec63 Split RFC3339 test 2018-05-15 09:13:01 +02:00
Timo Furrer 62a6283200 Merge pull request #150 from marcelstoer/patch-1
Fix RFC3339 representation
2018-05-15 09:08:52 +02:00
Marcel Stör 9ef43a29d9 Add RFC3339 unit test 2018-05-14 21:49:47 +02:00
Marcel Stör a2bff42439 Fix RFC3339 representation
Must be at most 1-digit millisecond.
2018-05-14 20:48:28 +02:00
Timo Furrer 5525beda31 release: 0.4.2 2018-05-10 17:25:04 +02:00
Timo Furrer eeb07d46db Merge pull request #152 from kennethreitz/feature/local-datetime
Implement method to get MayaDT instance as local timezone-aware datetime instance
2018-05-10 17:24:44 +02:00
Timo Furrer 4056d1a9aa Implement method to get MayaDT instance as local timezone-aware datetime instance 2018-05-10 17:22:35 +02:00
Timo Furrer 7a750a1cff release: 0.4.1 2018-05-10 11:32:54 +02:00
Timo Furrer bc06315abd Limit pendulum version to >=1.0 and <= 1.5.1
See https://github.com/sdispater/pendulum/issues/205
2018-05-10 11:32:32 +02:00
Timo Furrer 7cc767781a release: 0.4.0 2018-05-10 11:13:55 +02:00
Timo Furrer 8468dd2ead Fix subtracting MayaDT instances. Fixes #151 2018-05-10 11:04:16 +02:00
kennethreitz 046f005ca7 Update README.rst 2018-05-08 08:05:07 -04:00
kennethreitz cd0b2300d7 Merge pull request #128 from azban/patch-1
readme: add artist attribution
2018-05-08 08:04:05 -04:00
Timo Furrer d3ddb39d9d Merge pull request #144 from kennethreitz/feature/cleanup-pipenv-use
Refactor pipenv usage according to semi-official best practices.
2018-05-08 08:53:31 +02:00
Timo Furrer 3c8fe4478c Merge pull request #147 from alxwrd/fix-when-weekday-names
.when with weekdays and prefer_past
2018-04-17 17:58:27 +02:00
Alex Ward a0983132bb update .when docstring 2018-04-16 12:20:14 +01:00
Alex Ward 9612d70707 update .when tests to use new prefer_dates_from param 2018-04-16 12:06:12 +01:00
Alex Ward 363fae1aaf update .when to use prefer_dates_from to align with dateparser 2018-04-16 11:39:16 +01:00
Alex Ward be2cd96132 add more options to .when pefer_past param 2018-04-15 19:22:56 +01:00
Alex Ward 771a2b6ce2 add tests around weekday names 2018-04-15 17:31:06 +01:00
Timo Furrer 8e644f655a Merge pull request #145 from vlcinsky/pendulum_ver
fix `pendulum>=1.0` in setup.py (was missing version)
2018-04-05 10:17:55 +02:00
Jan Vlcinsky 6903f6eb63 fix pendulum>=1.0 in setup.py (was missing version)
Assuming, that when Pipfile declares such rule, it shall be also in
setup.py.
2018-04-05 00:50:53 +02:00
Timo Furrer ca076ff625 Refactor pipenv usage according to semi-official best practices. Closes #142 2018-04-04 20:42:13 +02:00
Jan Vlčinský e0e33cc29f Remove probably obsolete requirement for ruamel.yaml (#136)
* Remove probably obsolete requirement for ruamel.yaml

Closes issue #134

* require dateparser>=0.7.0 (to ensure ruamel.yaml is not really needed)
2018-04-04 13:37:31 +02:00
Timo Furrer bd34915f96 Merge pull request #135 from vlcinsky/frozen_time
test with "now" frozen to different times/zones
2018-04-03 20:08:57 +02:00
Jan Vlcinsky 16690cfceb Removed Pipfile.lock 2018-04-03 19:57:58 +02:00
Jan Vlcinsky 97af369f05 Pipfile.lock updated with --dev (freezegun) 2018-04-03 19:54:18 +02:00
Jan Vlcinsky 367dac4e62 number of frozen_now variants narrowed to speed up tests 2018-04-03 19:54:18 +02:00
Jan Vlcinsky 59ec275ef6 test with "now" frozen to different times/zones
Using freezegun to freeze current times.
2018-04-03 19:54:18 +02:00
Timo Furrer 76f99bc781 Merge pull request #141 from vlcinsky/simple_pytest
Simplified call to pytest in tox.ini (issue #139)
2018-04-03 16:40:26 +02:00
Timo Furrer bf56321545 Merge branch 'master' into simple_pytest 2018-04-03 16:35:23 +02:00
Timo Furrer ee602ea56b Merge pull request #140 from vlcinsky/tox_ini
Get rid of Pipfile.lock (issue #138)
2018-04-03 16:34:35 +02:00
Jan Vlcinsky df3bdf231d Simplified call to pytest in tox.ini (issue #139) 2018-04-02 22:50:14 +02:00
Jan Vlcinsky 1a58d4f710 Get rid of Pipfile.lock (issue #138) 2018-04-02 22:43:28 +02:00
Timo Furrer d0d5a8f75d Explicitly specifiy time struct when getting UTC offset for current time 2018-04-02 12:09:31 +02:00
Timo Furrer 00a0ceb3c8 Add tox.ini for local testing. Refs #137 2018-04-01 14:09:02 +02:00
Timo Furrer ee0fe3b810 Merge pull request #131 from vlcinsky/master
test_maya.py refactored: using parametrization where feasible
2018-03-31 11:50:32 +02:00
Timo Furrer 0e0815c45a Merge pull request #132 from kennethreitz/feature/python-3.7
Add Python 3.7 to test matrix
2018-03-31 11:49:07 +02:00
Timo Furrer 04a82f3078 Add Python 3.7 to test matrix 2018-03-31 11:46:05 +02:00
Timo Furrer 5f6b5fc66d Use UTC offset from given time struct when converting from time struct. Fixes #104 2018-03-31 11:42:07 +02:00
Jan Vlcinsky e9592a3146 test_maya.py refactored: using parametrization where feasible
repeated code replaced by parameters provided by pytest.

source string and rexpected intentionally indented to match by column
to make checking year, month and date simpler.

Note, that the test_issue_104 was (and still is) failing.
2018-03-31 01:31:14 +02:00
Timo Furrer 15a5c9eedb Merge pull request #130 from bsdtux/master
PR for issue #129. Attempt to secure _EPOCH_START
2018-03-30 11:19:14 +02:00
Josh Stephens 7c5d5871d3 * moved _EPOCH_START to be a class variable to protect from a user
makeing an arbitrary change which could render inaccurate results from MayaDT class
2018-03-25 16:06:38 -05:00
azban b05ca8707c readme: add artist attribution 2018-03-21 10:50:24 -07:00
kennethreitz b411d5e6d1 Merge pull request #127 from matmunn/duration
Parsing an ISO8601 duration for an interval
2018-03-21 06:30:04 -04:00
Mat Munn 5458b0bf15 Parsing an ISO8601 duration for an interval 2018-03-21 11:23:22 +11:00
Timo Furrer 02f91de523 Merge pull request #124 from matmunn/master
Basic implementation of ISO 8601 parsing for MayaInterval
2018-03-18 12:30:41 +01:00
Mat Munn 00d174c9e2 Merge branch 'master' into master 2018-03-18 08:04:51 +11:00
Mat Munn f87f705a53 Added test 2018-03-18 08:03:34 +11:00
Timo Furrer 9a850b4212 PEP8 formatting by white
Used `white maya/ tests/`.
2018-03-17 12:24:18 +01:00
Timo Furrer 84a5f700c3 Merge pull request #126 from timofurrer/feature/snap
Implement snap modifiers with maya.snap(). Closes #94
2018-03-17 11:59:45 +01:00
Timo Furrer 13e9f2d896 Implement snap modifiers with maya.snap(). Closes #94 2018-03-17 11:55:18 +01:00
Timo Furrer 0c206ad0db Fix tests 2018-03-17 11:52:47 +01:00
Timo Furrer aa87723a0c Make tests and docs a phony makefile target 2018-03-14 20:17:35 +01:00
Mat Munn d51a6a57ec Basic implementation of ISO 8601 parsing for MayaInterval 2018-03-14 10:13:50 +11:00
kennethreitz 3cb65f227c setup.py update
Signed-off-by: Kenneth Reitz <me@kennethreitz.org>
2018-02-26 17:57:57 -05:00
15 changed files with 771 additions and 772 deletions
+1
View File
@@ -24,6 +24,7 @@ wheels/
*.egg-info/
.installed.cfg
*.egg
Pipfile.lock
# PyInstaller
# Usually these files are written by a python script from a template
+10 -1
View File
@@ -2,8 +2,17 @@ language: python
python:
- "2.7"
- "3.6"
- "3.7-dev"
matrix:
allow_failures:
- python: "3.7-dev"
# command to install dependencies
install: pip install pipenv; pipenv lock; pipenv install --dev
install:
- pip install pipenv
- pipenv install '-e .' --skip-lock --ignore-pipfile
- pipenv install --dev --skip-lock
# command to run tests
script: pipenv run pytest tests/
+1
View File
@@ -24,3 +24,4 @@ In chronological order:
- Evan Mattiza <emattiza@gmail.com> (`@emattiza <https://github.com/emattiza>`_)
- Dima Spivak <dima@spivak.ch> (`@dimaspivak <https://github.com/dimaspivak>`_)
- Tom Barron <tusculum@gmail.com> (`@dtbarron <https://github.com/tbarron>`_)
- Alex Ward <alxwrd@gmail.com> (`@alxwrd <https://github.com/alxwrd>`_)
+2
View File
@@ -1,3 +1,5 @@
.PHONY: tests docs
tests:
pytest tests/
docs:
+2 -9
View File
@@ -1,11 +1,4 @@
[dev-packages]
freezegun = "*"
pytest = "*"
Sphinx = "*"
[packages]
humanize = "*"
pytz = "*"
dateparser = "*"
"ruamel.yaml" = "*"
tzlocal = "*"
pendulum = ">=1.0"
sphinx = "*"
Generated
-295
View File
@@ -1,295 +0,0 @@
{
"_meta": {
"hash": {
"sha256": "e7c76d32bf7d24c6e3637eb39049c1543ba59729fe50da193414bde84a120587"
},
"host-environment-markers": {
"implementation_name": "cpython",
"implementation_version": "3.6.1",
"os_name": "posix",
"platform_machine": "x86_64",
"platform_python_implementation": "CPython",
"platform_release": "17.3.0",
"platform_system": "Darwin",
"platform_version": "Darwin Kernel Version 17.3.0: Thu Nov 9 18:09:22 PST 2017; root:xnu-4570.31.3~1/RELEASE_X86_64",
"python_full_version": "3.6.1",
"python_version": "3.6",
"sys_platform": "darwin"
},
"pipfile-spec": 6,
"requires": {},
"sources": [
{
"name": "pypi",
"url": "https://pypi.python.org/simple",
"verify_ssl": true
}
]
},
"default": {
"dateparser": {
"hashes": [
"sha256:e2629d2f8361722c6047138ca085256c9f2cf5cc657fd66122aa0564afa4dc33",
"sha256:f8c24317120b06f71691d28076764ec084a132be2a250a78fdf54f6b427cac95"
],
"version": "==0.6.0"
},
"humanize": {
"hashes": [
"sha256:a43f57115831ac7c70de098e6ac46ac13be00d69abbf60bdcac251344785bb19"
],
"version": "==0.5.1"
},
"pendulum": {
"hashes": [
"sha256:9196f0aa4eec534aaf02b45c47dccc6f74a255ecdab6c772cb6bcea6b22790e4",
"sha256:a34690d8d4fc8eab34ea2dd9a99482dbbf0b1f059fe25effe204dd59bceda069",
"sha256:1577a44b1f4bbc942136bce654df24e31735e1ff6aaa37e0a6207abf13868be9",
"sha256:f68f0f13498c9350ffc712765c4e0bdc824a4afd767d1a71933ff7be380bf75e",
"sha256:76d6861664126fef4cbbdc6218ca09d81c4ed8da4c6df9637e0069f7d820f901",
"sha256:327c89477e6ea0e240cd9f94c241747a534ac6f3e71c9b2f3298485ffc8939b2",
"sha256:4f1675010fd934aea011642c33c0dd9bc6954d9be7032c7f9ccfea1ac8d752d7",
"sha256:e996c34fb101c9c6d88a839c19af74d7c067b92ed3371274efcf4d4b6dc160a6"
],
"version": "==1.4.0"
},
"python-dateutil": {
"hashes": [
"sha256:95511bae634d69bc7329ba55e646499a842bc4ec342ad54a8cdb65645a0aad3c",
"sha256:891c38b2a02f5bb1be3e4793866c8df49c7d19baabf9c1bad62547e0b4866aca"
],
"version": "==2.6.1"
},
"pytz": {
"hashes": [
"sha256:80af0f3008046b9975242012a985f04c5df1f01eed4ec1633d56cc47a75a6a48",
"sha256:feb2365914948b8620347784b6b6da356f31c9d03560259070b2f30cff3d469d",
"sha256:59707844a9825589878236ff2f4e0dc9958511b7ffaae94dc615da07d4a68d33",
"sha256:d0ef5ef55ed3d37854320d4926b04a4cb42a2e88f71da9ddfdacfde8e364f027",
"sha256:c41c62827ce9cafacd6f2f7018e4f83a6f1986e87bfd000b8cfbd4ab5da95f1a",
"sha256:8cc90340159b5d7ced6f2ba77694d946fc975b09f1a51d93f3ce3bb399396f94",
"sha256:dd2e4ca6ce3785c8dd342d1853dd9052b19290d5bf66060846e5dc6b8d6667f7",
"sha256:699d18a2a56f19ee5698ab1123bbcc1d269d061996aeb1eda6d89248d3542b82",
"sha256:fae4cffc040921b8a2d60c6cf0b5d662c1190fe54d718271db4eb17d44a185b7"
],
"version": "==2017.3"
},
"pytzdata": {
"hashes": [
"sha256:cd5b72400a7378b3b45eef5929cbe97ed44c3368685c35c477e316ebaa7e1809",
"sha256:e87376f2ee7cb89af5ddea5ed07ce3e98a55f891d07ae87d8c49e99f069423f2"
],
"version": "==2017.3.1"
},
"regex": {
"hashes": [
"sha256:28a542117efd479cff110711c28f34cb40636f670d427dc42710b5183fbd92c4",
"sha256:93064faef899911a7378443fcfb1f2e93e19706a324162fc71dd996c85b24f37",
"sha256:9c4b23f8a68470522a0cdfbdcffbe2f61d7659b5d505cb7c1a729a6805345210",
"sha256:45fed2f98e5ca0fc420ff31cbd9eccbee41e4d607569fddaba0faea36ef6043d",
"sha256:079c656874ba6c2e3332df488d4eff33b94b4e260e3901dce1553cb4f1f36c73",
"sha256:c1ef8f72944bef49261daaa83c7d77439f54f8deefefc0e2cf7144ac8b20ac1f",
"sha256:26795107acb81bb07ef832832548834b03846eb046e5ba9a5917ee012c68c1e6",
"sha256:3e2609e0e366b21c3db3d41159d99e7bd37e02caaba24bba77ec5cc594c62c4e",
"sha256:801e054c1aa163545d29e186a1bea779437a19b49c4da9e11049624c2d2bfd31",
"sha256:51306abdaac9e712b208066d284ddc7e3a332c77ad6054ba8d305d607609a328",
"sha256:660990e223ef2f71cb78b4e106a9a023652a31fd305051a901b0f87171b69e24",
"sha256:47230e1af3479810b1ef2bf23768b5195588a03eea6248b678d6893c48f58082",
"sha256:5f15a27c24ed4ad2ed492abe80ddf27d35d63ef6c0d8878c915de6bf9f36c6f3",
"sha256:67025161b70b0625749b1b89200da59fecc2fda9d1e46f9ef588f9a4af9fc48f",
"sha256:139678fc013b75e486e580c39b4c52d085ed7362e400960f8be1711a414f16b5"
],
"version": "==2018.1.10"
},
"ruamel.yaml": {
"hashes": [
"sha256:14d161558e3bf89e87d77c218098be22fa9a0d6d0bea40250fce525b1d0cbee2",
"sha256:fcfc24a21594c071cc4588e84b7657a1f47ebcf6037c6c43fa15c4bbd3989ec2",
"sha256:02babffd019911841ba01b76e23dfec7c9e9b2725503fb2698c4982fa1a6e835",
"sha256:c0908896e34b617ead40552cab03c1769bdc43d1da02419160dc900c5dfddde2",
"sha256:01e30ecb1b1c0ebf9fce814dc20dace402571517277799291202b61b22096c24",
"sha256:b6c5d5f03ba78e3f27c7188a00c4e09b6a4507fe3154ba40a294e09cb30ee016",
"sha256:9225c83952d28f302cfc23c3d9a6f8231bfd581476d7aff1e3c7de49eecb4ee9",
"sha256:c41e04b526d0153c9246cfab87d7ddefdc9f165cb8886a8ec48ba7a2b73069f6",
"sha256:6d05c5a5baf829c70916c226ef3200650846a7227de226bca8a59efaf88bb973",
"sha256:e3bbfe0d294e08fdbb0cb05485435a2ceb4e168e98b5dc611f051c1864986b4b",
"sha256:68c8f2986bcb91b6db1aea8698941769840c7257e951a9377048f7eff35be773",
"sha256:072f6364a89972e8dc0afdce3335a709d5464dfeaa4f736d092a54574338b874",
"sha256:5504398fc755a2b14c9983b2101161a8591a4b30812590cc1c365e7fcc117dfa",
"sha256:e2d2715bf92156bec5fb42e92e95dac1c4d9904f8a3d4e2d0c438758fe9092d7",
"sha256:6d7929b24e329d662fa43b657fddfee5260e2d35d0a543065cd755d4e17a9b2f",
"sha256:f2d02a4af5a13b09d0b823cdd0317b54f3e0115e50b5ac4d9840c3a1b566817f",
"sha256:8dc74821e4bb6b21fb1ab35964e159391d99ee44981d07d57bf96e2395f3ef75"
],
"version": "==0.15.35"
},
"six": {
"hashes": [
"sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb",
"sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9"
],
"version": "==1.11.0"
},
"tzlocal": {
"hashes": [
"sha256:4ebeb848845ac898da6519b9b31879cf13b6626f7184c496037b818e238f2c4e"
],
"version": "==1.5.1"
}
},
"develop": {
"alabaster": {
"hashes": [
"sha256:2eef172f44e8d301d25aff8068fddd65f767a3f04b5f15b0f4922f113aa1c732",
"sha256:37cdcb9e9954ed60912ebc1ca12a9d12178c26637abdf124e3cde2341c257fe0"
],
"version": "==0.7.10"
},
"attrs": {
"hashes": [
"sha256:a17a9573a6f475c99b551c0e0a812707ddda1ec9653bed04c13841404ed6f450",
"sha256:1c7960ccfd6a005cd9f7ba884e6316b5e430a3f1a6c37c5f87d8b43f83b54ec9"
],
"version": "==17.4.0"
},
"babel": {
"hashes": [
"sha256:ad209a68d7162c4cff4b29cdebe3dec4cef75492df501b0049a9433c96ce6f80",
"sha256:8ce4cb6fdd4393edd323227cba3a077bceb2a6ce5201c902c65e730046f41f14"
],
"version": "==2.5.3"
},
"certifi": {
"hashes": [
"sha256:14131608ad2fd56836d33a71ee60fa1c82bc9d2c8d98b7bdbc631fe1b3cd1296",
"sha256:edbc3f203427eef571f79a7692bb160a2b0f7ccaa31953e99bd17e307cf63f7d"
],
"version": "==2018.1.18"
},
"chardet": {
"hashes": [
"sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691",
"sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae"
],
"version": "==3.0.4"
},
"docutils": {
"hashes": [
"sha256:7a4bd47eaf6596e1295ecb11361139febe29b084a87bf005bf899f9a42edc3c6",
"sha256:02aec4bd92ab067f6ff27a38a38a41173bf01bed8f89157768c1573f53e474a6",
"sha256:51e64ef2ebfb29cae1faa133b3710143496eca21c530f3f71424d77687764274"
],
"version": "==0.14"
},
"idna": {
"hashes": [
"sha256:8c7309c718f94b3a625cb648ace320157ad16ff131ae0af362c9f21b80ef6ec4",
"sha256:2c6a5de3089009e3da7c5dde64a141dbc8551d5b7f6cf4ed7c2568d0cc520a8f"
],
"version": "==2.6"
},
"imagesize": {
"hashes": [
"sha256:6ebdc9e0ad188f9d1b2cdd9bc59cbe42bf931875e829e7a595e6b3abdc05cdfb",
"sha256:0ab2c62b87987e3252f89d30b7cedbec12a01af9274af9ffa48108f2c13c6062"
],
"version": "==0.7.1"
},
"jinja2": {
"hashes": [
"sha256:74c935a1b8bb9a3947c50a54766a969d4846290e1e788ea44c1392163723c3bd",
"sha256:f84be1bb0040caca4cea721fcbbbbd61f9be9464ca236387158b0feea01914a4"
],
"version": "==2.10"
},
"markupsafe": {
"hashes": [
"sha256:a6be69091dac236ea9c6bc7d012beab42010fa914c459791d627dad4910eb665"
],
"version": "==1.0"
},
"pluggy": {
"hashes": [
"sha256:7f8ae7f5bdf75671a718d2daf0a64b7885f74510bcd98b1a0bb420eb9a9d0cff"
],
"version": "==0.6.0"
},
"py": {
"hashes": [
"sha256:8cca5c229d225f8c1e3085be4fcf306090b00850fefad892f9d96c7b6e2f310f",
"sha256:ca18943e28235417756316bfada6cd96b23ce60dd532642690dcfdaba988a76d"
],
"version": "==1.5.2"
},
"pygments": {
"hashes": [
"sha256:78f3f434bcc5d6ee09020f92ba487f95ba50f1e3ef83ae96b9d5ffa1bab25c5d",
"sha256:dbae1046def0efb574852fab9e90209b23f556367b5a320c0bcb871c77c3e8cc"
],
"version": "==2.2.0"
},
"pytest": {
"hashes": [
"sha256:b84878865558194630c6147f44bdaef27222a9f153bbd4a08908b16bf285e0b1",
"sha256:53548280ede7818f4dc2ad96608b9f08ae2cc2ca3874f2ceb6f97e3583f25bc4"
],
"version": "==3.3.2"
},
"pytz": {
"hashes": [
"sha256:80af0f3008046b9975242012a985f04c5df1f01eed4ec1633d56cc47a75a6a48",
"sha256:feb2365914948b8620347784b6b6da356f31c9d03560259070b2f30cff3d469d",
"sha256:59707844a9825589878236ff2f4e0dc9958511b7ffaae94dc615da07d4a68d33",
"sha256:d0ef5ef55ed3d37854320d4926b04a4cb42a2e88f71da9ddfdacfde8e364f027",
"sha256:c41c62827ce9cafacd6f2f7018e4f83a6f1986e87bfd000b8cfbd4ab5da95f1a",
"sha256:8cc90340159b5d7ced6f2ba77694d946fc975b09f1a51d93f3ce3bb399396f94",
"sha256:dd2e4ca6ce3785c8dd342d1853dd9052b19290d5bf66060846e5dc6b8d6667f7",
"sha256:699d18a2a56f19ee5698ab1123bbcc1d269d061996aeb1eda6d89248d3542b82",
"sha256:fae4cffc040921b8a2d60c6cf0b5d662c1190fe54d718271db4eb17d44a185b7"
],
"version": "==2017.3"
},
"requests": {
"hashes": [
"sha256:6a1b267aa90cac58ac3a765d067950e7dbbf75b1da07e895d1f594193a40a38b",
"sha256:9c443e7324ba5b85070c4a818ade28bfabedf16ea10206da1132edaa6dda237e"
],
"version": "==2.18.4"
},
"six": {
"hashes": [
"sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb",
"sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9"
],
"version": "==1.11.0"
},
"snowballstemmer": {
"hashes": [
"sha256:9f3bcd3c401c3e862ec0ebe6d2c069ebc012ce142cce209c098ccb5b09136e89",
"sha256:919f26a68b2c17a7634da993d91339e288964f93c274f1343e3bbbe2096e1128"
],
"version": "==1.2.1"
},
"sphinx": {
"hashes": [
"sha256:b8baed19394af85b21755c68c7ec4eac57e8a482ed89cd01cd5d5ff72200fe0f",
"sha256:c39a6fa41bd3ec6fc10064329a664ed3a3ca2e27640a823dc520c682e4433cdb"
],
"version": "==1.6.6"
},
"sphinxcontrib-websupport": {
"hashes": [
"sha256:f4932e95869599b89bf4f80fc3989132d83c9faa5bf633e7b5e0c25dffb75da2",
"sha256:7a85961326aa3a400cd4ad3c816d70ed6f7c740acd7ce5d78cd0a67825072eb9"
],
"version": "==1.0.1"
},
"urllib3": {
"hashes": [
"sha256:06330f386d6e4b195fbfc736b297f58c5a892e4440e54d294d7004e3a9bbea1b",
"sha256:cc44da8e1145637334317feebd728bd869a35285b93cbb4cca2577da7e62db4f"
],
"version": "==1.22"
}
}
}
+21 -8
View File
@@ -22,6 +22,10 @@ Maya is mostly built around the headaches and use-cases around parsing datetime
.. image:: https://farm4.staticflickr.com/3702/33288285996_5b69d2b8f7_k_d.jpg
Art by `Sam Flores
<https://www.instagram.com/samagram12/>`_ (Photo by `Kenneth Reitz
<https://www.instagram.com/kennethreitz/>`_).
☤ Basic Usage of Maya
---------------------
@@ -69,10 +73,14 @@ Behold, datetimes for humans!
>>> m = maya.MayaDT.from_datetime(datetime.utcnow())
>>> print(m)
Wed, 20 Sep 2017 17:24:32 GMT
>>> m = maya.MayaDT.from_struct(time.gmtime())
>>> print(m)
Wed, 20 Sep 2017 17:24:32 GMT
Wed, 20 Sep 2017 17:24:32 GMT
>>> m = maya.MayaDT(time.time())
>>> print(m)
Wed, 20 Sep 2017 17:24:32 GMT
>>> rand_day.day
7
@@ -88,10 +96,15 @@ Behold, datetimes for humans!
>>> maya.intervals(start=maya.now(), end=maya.now().add(days=1), interval=60*60)
<generator object intervals at 0x105ba5820>
# snap modifiers
>>> dt = maya.when('Mon, 21 Feb 1994 21:21:42 GMT')
>>> dt.snap('@d+3h').rfc2822()
'Mon, 21 Feb 1994 03:00:00 GMT'
☤ Advanced Usage of Maya
------------------------
In addition to timestamps, Maya also includes a wonderfuly powerful ``MayaInterval`` class, which represents a range of time (e.g. an event). With this class, you can perform a multitude of advanced calendar calculations with finesse and ease.
In addition to timestamps, Maya also includes a wonderfully powerful ``MayaInterval`` class, which represents a range of time (e.g. an event). With this class, you can perform a multitude of advanced calendar calculations with finesse and ease.
For example:
@@ -99,15 +112,15 @@ For example:
>>> from maya import MayaInterval
# Create an event that is one hour long, starting now.
# Create an event that is one hour long, starting now.
>>> event_start = maya.now()
>>> event_end = event_start.add(hours=1)
>>> event = MayaInterval(start=event_start, end=event_end)
From here, there are a number of methods available to you, which you can use to compare this event to another event.
☤ Why is this useful?
---------------------
@@ -124,7 +137,7 @@ From here, there are a number of methods available to you, which you can use to
☤ What about Delorean, Arrow, & Pendulum?
-----------------------------------------
All these project complement each other, and are friends. Pendulum, for example, helps power Maya's parsing.
All these project complement each other, and are friends. Pendulum, for example, helps power Maya's parsing.
Arrow, for example, is a fantastic library, but isn't what I wanted in a datetime library. In many ways, it's better than Maya for certain things. In some ways, in my opinion, it's not.
+1 -1
View File
@@ -1 +1 @@
__version__ = '0.3.4'
__version__ = '0.5.0'
+11 -9
View File
@@ -1,5 +1,4 @@
# -*- coding: utf-8 -*-
"""
maya.compat
~~~~~~~~~~~~~~~
@@ -12,24 +11,19 @@ import sys
# -------
# Pythons
# -------
# Syntax sugar.
_ver = sys.version_info
#: Python 2.x?
# : Python 2.x?
is_py2 = (_ver[0] == 2)
#: Python 3.x?
# : Python 3.x?
is_py3 = (_ver[0] == 3)
# ---------
# Specifics
# ---------
if is_py2:
cmp = cmp
elif is_py3:
def cmp(a, b):
"""
Compare two objects.
@@ -38,8 +32,10 @@ elif is_py3:
"""
if a < b:
return -1
elif a == b:
return 0
else:
return 1
@@ -59,36 +55,42 @@ def comparable(klass):
c = self.__cmp__(other)
if c is NotImplemented:
return c
return c == 0
def __ne__(self, other):
c = self.__cmp__(other)
if c is NotImplemented:
return c
return c != 0
def __lt__(self, other):
c = self.__cmp__(other)
if c is NotImplemented:
return c
return c < 0
def __le__(self, other):
c = self.__cmp__(other)
if c is NotImplemented:
return c
return c <= 0
def __gt__(self, other):
c = self.__cmp__(other)
if c is NotImplemented:
return c
return c > 0
def __ge__(self, other):
c = self.__cmp__(other)
if c is NotImplemented:
return c
return c >= 0
klass.__lt__ = __lt__
+258 -125
View File
@@ -1,28 +1,23 @@
# ___ __ ___ _ _ ___
# || \/ | ||=|| \\// ||=||
# || | || || // || ||
# Ignore warnings for yaml usage.
import warnings
import ruamel.yaml
warnings.simplefilter('ignore', ruamel.yaml.error.UnsafeLoaderWarning)
import email.utils
import time
import functools
from datetime import timedelta, datetime as Datetime
import functools
import re
import pytz
import humanize
import dateparser
import pendulum
import snaptime
from tzlocal import get_localzone
from dateutil.relativedelta import relativedelta
from dateparser.languages.loader import default_loader
from .compat import cmp, comparable
_EPOCH_START = (1970, 1, 1)
def validate_class_type_arguments(operator):
"""
@@ -31,14 +26,20 @@ def validate_class_type_arguments(operator):
"""
def inner(function):
def wrapper(self, *args, **kwargs):
for arg in args + tuple(kwargs.values()):
if not isinstance(arg, self.__class__):
raise TypeError('unorderable types: {}() {} {}()'.format(
type(self).__name__, operator, type(arg).__name__))
raise TypeError(
'unorderable types: {}() {} {}()'.format(
type(self).__name__, operator, type(arg).__name__
)
)
return function(self, *args, **kwargs)
return wrapper
return inner
@@ -51,26 +52,34 @@ def validate_arguments_type_of_function(param_type=None):
Note: Use this decorator on the functions of the class.
"""
def inner(function):
def wrapper(self, *args, **kwargs):
type_ = param_type or type(self)
for arg in args + tuple(kwargs.values()):
if not isinstance(arg, type_):
raise TypeError(('Invalid Type: {}.{}() accepts only the '
'arguments of type "<{}>"').format(
type(self).__name__,
function.__name__,
type_.__name__,
)
)
raise TypeError(
(
'Invalid Type: {}.{}() accepts only the '
'arguments of type "<{}>"'
).format(
type(self).__name__,
function.__name__,
type_.__name__,
)
)
return function(self, *args, **kwargs)
return wrapper
return inner
class MayaDT(object):
"""The Maya Datetime object."""
__EPOCH_START = (1970, 1, 1)
def __init__(self, epoch):
super(MayaDT, self).__init__()
@@ -114,7 +123,9 @@ class MayaDT(object):
return hash(int(self.epoch))
def __add__(self, duration):
return self.add(seconds=_seconds_or_timedelta(duration).total_seconds())
return self.add(
seconds=_seconds_or_timedelta(duration).total_seconds()
)
def __radd__(self, duration):
return self + duration
@@ -122,25 +133,39 @@ class MayaDT(object):
def __sub__(self, duration_or_date):
if isinstance(duration_or_date, MayaDT):
return self.subtract_date(dt=duration_or_date)
else:
return self.subtract(
seconds=_seconds_or_timedelta(duration_or_date).total_seconds())
seconds=_seconds_or_timedelta(duration_or_date).total_seconds()
)
def add(self, **kwargs):
"""Returns a new MayaDT object with the given offsets."""
return self.from_datetime(pendulum.instance(self.datetime()).add(**kwargs))
return self.from_datetime(
pendulum.instance(self.datetime()).add(**kwargs)
)
def subtract(self, **kwargs):
"""Returns a new MayaDT object with the given offsets."""
return self.from_datetime(pendulum.instance(self.datetime()).subtract(**kwargs))
return self.from_datetime(
pendulum.instance(self.datetime()).subtract(**kwargs)
)
def subtract_date(self, **kwargs):
"""Returns a timedelta object with the duration between the dates"""
return timedelta(self.epoch - kwargs['dt'].epoch)
return timedelta(seconds=self.epoch - kwargs['dt'].epoch)
def snap(self, instruction):
"""
Returns a new MayaDT object modified by the given instruction.
Powered by snaptime. See https://github.com/zartstrom/snaptime
for a complete documentation about the snaptime instructions.
"""
return self.from_datetime(snaptime.snap(self.datetime(), instruction))
# Timezone Crap
# -------------
@property
def timezone(self):
"""Returns the UTC tzinfo name. It's always UTC. Always."""
@@ -153,9 +178,10 @@ class MayaDT(object):
@property
def local_timezone(self):
"""Returns the name of the local timezone, for informational purposes."""
"""Returns the name of the local timezone."""
if self._local_tz.zone in pytz.all_timezones:
return self._local_tz.zone
return self.timezone
@property
@@ -167,17 +193,14 @@ class MayaDT(object):
@validate_arguments_type_of_function(Datetime)
def __dt_to_epoch(dt):
"""Converts a datetime into an epoch."""
# Assume UTC if no datetime is provided.
if dt.tzinfo is None:
dt = dt.replace(tzinfo=pytz.utc)
epoch_start = Datetime(*_EPOCH_START, tzinfo=pytz.timezone('UTC'))
epoch_start = Datetime(*MayaDT.__EPOCH_START, tzinfo=pytz.timezone('UTC'))
return (dt - epoch_start).total_seconds()
# Importers
# ---------
@classmethod
@validate_arguments_type_of_function(Datetime)
def from_datetime(klass, dt):
@@ -187,8 +210,11 @@ class MayaDT(object):
@classmethod
@validate_arguments_type_of_function(time.struct_time)
def from_struct(klass, struct, timezone=pytz.UTC):
"""Returns MayaDT instance from a 9-tuple struct (assumed to be from gmtime())"""
struct_time = time.mktime(struct) - utc_offset()
"""Returns MayaDT instance from a 9-tuple struct
It's assumed to be from gmtime().
"""
struct_time = time.mktime(struct) - utc_offset(struct)
dt = Datetime.fromtimestamp(struct_time, timezone)
return klass(klass.__dt_to_epoch(dt))
@@ -209,30 +235,37 @@ class MayaDT(object):
# Exporters
# ---------
def datetime(self, to_timezone=None, naive=False):
"""Returns a timezone-aware datetime...
Defaulting to UTC (as it should).
Keyword Arguments:
to_timezone {string} -- timezone to convert to (default: None/UTC)
naive {boolean} -- if True, the tzinfo is simply dropped (default: False)
to_timezone {str} -- timezone to convert to (default: None/UTC)
naive {bool} -- if True,
the tzinfo is simply dropped (default: False)
"""
if to_timezone:
dt = self.datetime().astimezone(pytz.timezone(to_timezone))
else:
dt = Datetime.utcfromtimestamp(self._epoch)
dt.replace(tzinfo=self._tz)
# Strip the timezone info if requested to do so.
if naive:
return dt.replace(tzinfo=None)
else:
if dt.tzinfo is None:
dt = dt.replace(tzinfo=self._tz)
return dt
def local_datetime(self):
"""Returns a local timezone-aware datetime object
It's the same as:
mayaDt.datetime(to_timezone=mayaDt.local_timezone)
"""
return self.datetime(to_timezone=self.local_timezone, naive=False)
def iso8601(self):
"""Returns an ISO 8601 representation of the MayaDT."""
# Get a timezone-naive datetime.
@@ -245,11 +278,10 @@ class MayaDT(object):
def rfc3339(self):
"""Returns an RFC 3339 representation of the MayaDT."""
return self.datetime().strftime("%Y-%m-%dT%H:%M:%S.%f")[:-4] + "Z"
return self.datetime().strftime("%Y-%m-%dT%H:%M:%S.%f")[:-5] + "Z"
# Properties
# ----------
@property
def year(self):
return self.datetime().year
@@ -261,7 +293,7 @@ class MayaDT(object):
@property
def day(self):
return self.datetime().day
@property
def date(self):
return self.datetime().date()
@@ -272,7 +304,10 @@ class MayaDT(object):
@property
def weekday(self):
"""Return the day of the week as an integer. Monday is 1 and Sunday is 7"""
"""Return the day of the week as an integer.
Monday is 1 and Sunday is 7.
"""
return self.datetime().isoweekday()
@property
@@ -297,21 +332,55 @@ class MayaDT(object):
# Human Slang Extras
# ------------------
def slang_date(self, locale="en"):
""""Returns human slang representation of date.
def slang_date(self):
""""Returns human slang representation of date."""
dt = self.datetime(naive=True, to_timezone=self.local_timezone)
return humanize.naturaldate(dt)
Keyword Arguments:
locale -- locale to translate to, e.g. 'fr' for french.
(default: 'en' - English)
"""
dt = pendulum.instance(self.datetime())
def slang_time(self):
""""Returns human slang representation of time."""
dt = self.datetime(naive=True, to_timezone=self.local_timezone)
return humanize.naturaltime(dt)
try:
return _translate(dt, locale)
except KeyError:
pass
dt.set_formatter("alternative")
delta = humanize.time.abs_timedelta(
timedelta(seconds=(self.epoch - now().epoch)))
format_string = "DD MMM"
if delta.days >= 365:
format_string += " YYYY"
return dt.format(format_string, locale=locale).title()
def slang_time(self, locale="en"):
""""Returns human slang representation of time.
Keyword Arguments:
locale -- locale to translate to, e.g. 'fr' for french.
(default: 'en' - English)
"""
dt = self.datetime()
return pendulum.instance(dt).diff_for_humans(locale=locale)
def utc_offset():
"""Returns the current local time offset from UTC accounting for DST """
ts = time.localtime()
def utc_offset(time_struct=None):
"""
Returns the time offset from UTC accounting for DST
Keyword Arguments:
time_struct {time.struct_time} -- the struct time for which to
return the UTC offset.
If None, use current local time.
"""
if time_struct:
ts = time_struct
else:
ts = time.localtime()
if ts[-1]:
offset = time.altzone
else:
@@ -322,12 +391,14 @@ def utc_offset():
def to_utc_offset_naive(dt):
if dt.tzinfo is None:
return dt
return dt.astimezone(pytz.utc).replace(tzinfo=None)
def to_utc_offset_aware(dt):
if dt.tzinfo is not None:
return dt
return pytz.utc.localize(dt)
@@ -336,40 +407,46 @@ def to_iso8601(dt):
def end_of_day_midnight(dt):
return dt if dt.time() == time.min else \
(dt.replace(hour=0, minute=0, second=0, microsecond=0) + timedelta(days=1))
if dt.time() == time.min:
return dt
else:
return (
dt.replace(hour=0, minute=0, second=0, microsecond=0) +
timedelta(days=1)
)
@comparable
class MayaInterval(object):
"""
A MayaInterval represents a range between two datetimes, inclusive of the start
and exclusive of the end.
A MayaInterval represents a range between two datetimes,
inclusive of the start and exclusive of the end.
"""
def __init__(self, start=None, end=None, duration=None):
try:
# Ensure that proper arguments were passed.
assert any((
(start and end),
(start and duration is not None),
(end and duration is not None),
))
assert any(
(
(start and end),
(start and duration is not None),
(end and duration is not None),
)
)
assert not all((start, end, duration is not None))
except AssertionError:
raise ValueError(
'Exactly 2 of start, end, and duration must be specified')
'Exactly 2 of start, end, and duration must be specified'
)
# Convert duration to timedelta if seconds were provided.
if duration:
duration = _seconds_or_timedelta(duration)
if not start:
start = end - duration
if not end:
end = start + duration
if start > end:
raise ValueError('MayaInterval cannot end before it starts')
@@ -377,28 +454,56 @@ class MayaInterval(object):
self.end = end
def __repr__(self):
return '<MayaInterval start={0!r} end={1!r}>'.format(self.start, self.end)
return '<MayaInterval start={0!r} end={1!r}>'.format(
self.start, self.end
)
def iso8601(self):
"""Returns an ISO 8601 representation of the MayaInterval."""
return '{0}/{1}'.format(self.start.iso8601(), self.end.iso8601())
@classmethod
def parse_iso8601_duration(cls, duration, start=None, end=None):
match = re.match(
r'(?:P(?P<weeks>\d+)W)|(?:P(?:(?:(?P<years>\d+)Y)?(?:(?P<months>\d+)M)?(?:(?P<days>\d+)D))?(?:T(?:(?P<hours>\d+)H)?(?:(?P<minutes>\d+)M)?(?:(?P<seconds>\d+)S)?)?)',
duration
)
time_components = {}
if match:
time_components = match.groupdict(0)
for key, value in time_components.items():
time_components[key] = int(value)
duration = relativedelta(**time_components)
if start:
return parse(start.datetime() + duration)
if end:
return parse(end.datetime() - duration)
return None
@classmethod
def from_iso8601(cls, s):
# # Start and end, such as "2007-03-01T13:00:00Z/2008-05-11T15:30:00Z"
# start, end = s.split('/')
# try:
# start = parse(start)
# except pendulum.parsing.exceptions.ParserError:
# start = self._parse_iso8601_duration(start)
# try:
# end = parse(start)
# except pendulum.parsing.exceptions.ParserError as e:
# end = self._parse_iso8601_duration(start)
start, end = s.split('/')
try:
start = parse(start)
except pendulum.parsing.exceptions.ParserError:
# start = self._parse_iso8601_duration(start, end=end)
raise NotImplementedError()
try:
end = parse(end)
except pendulum.parsing.exceptions.ParserError as e:
end = cls.parse_iso8601_duration(end, start=start)
return cls(start=start, end=end)
# # Start and duration, such as "2007-03-01T13:00:00Z/P1Y2M10DT2H30M"
# # Duration and end, such as "P1Y2M10DT2H30M/2008-05-11T15:30:00Z"
raise NotImplementedError()
@validate_arguments_type_of_function()
def __and__(self, maya_interval):
@@ -411,8 +516,7 @@ class MayaInterval(object):
@validate_arguments_type_of_function()
def __eq__(self, maya_interval):
return (
self.start == maya_interval.start and
self.end == maya_interval.end
self.start == maya_interval.start and self.end == maya_interval.end
)
def __hash__(self):
@@ -425,8 +529,8 @@ class MayaInterval(object):
@validate_arguments_type_of_function()
def __cmp__(self, maya_interval):
return (
cmp(self.start, maya_interval.start) or
cmp(self.end, maya_interval.end)
cmp(self.start, maya_interval.start)
or cmp(self.end, maya_interval.end)
)
@property
@@ -457,8 +561,9 @@ class MayaInterval(object):
MayaInterval(
interval_list[0].start,
max(interval_list[0].end, interval_list[1].end),
),
)
]
return interval_list
@validate_arguments_type_of_function()
@@ -466,6 +571,7 @@ class MayaInterval(object):
""""Removes the given interval."""
if not self & maya_interval:
return [self]
elif maya_interval.contains(self):
return []
@@ -477,10 +583,8 @@ class MayaInterval(object):
return interval_list
def split(self, duration, include_remainder=True):
# Convert seconds to timedelta, if appropriate.
duration = _seconds_or_timedelta(duration)
if duration <= timedelta(seconds=0):
raise ValueError('cannot call split with a non-positive timedelta')
@@ -488,38 +592,36 @@ class MayaInterval(object):
while start < self.end:
if start + duration <= self.end:
yield MayaInterval(start, start + duration)
elif include_remainder:
yield MayaInterval(start, self.end)
start += duration
def quantize(self, duration, snap_out=False, timezone='UTC'):
"""Returns a quanitzed interval."""
# Convert seconds to timedelta, if appropriate.
duration = _seconds_or_timedelta(duration)
timezone = pytz.timezone(timezone)
if duration <= timedelta(seconds=0):
raise ValueError('cannot quantize by non-positive timedelta')
epoch = timezone.localize(Datetime(1970, 1, 1))
seconds = int(duration.total_seconds())
start_seconds = int((self.start.datetime(naive=False) - epoch).total_seconds())
end_seconds = int((self.end.datetime(naive=False) - epoch).total_seconds())
start_seconds = int(
(self.start.datetime(naive=False) - epoch).total_seconds()
)
end_seconds = int(
(self.end.datetime(naive=False) - epoch).total_seconds()
)
if start_seconds % seconds and not snap_out:
start_seconds += seconds
if end_seconds % seconds and snap_out:
end_seconds += seconds
start_seconds -= start_seconds % seconds
end_seconds -= end_seconds % seconds
if start_seconds > end_seconds:
start_seconds = end_seconds
return MayaInterval(
start=MayaDT.from_datetime(epoch).add(seconds=start_seconds),
end=MayaDT.from_datetime(epoch).add(seconds=end_seconds),
@@ -528,23 +630,17 @@ class MayaInterval(object):
@validate_arguments_type_of_function()
def intersection(self, maya_interval):
"""Returns the intersection between two intervals."""
start = max(self.start, maya_interval.start)
end = min(self.end, maya_interval.end)
either_instant = self.is_instant or maya_interval.is_instant
instant_overlap = (
self.start == maya_interval.start or
start <= end
)
instant_overlap = (self.start == maya_interval.start or start <= end)
if (either_instant and instant_overlap) or (start < end):
return MayaInterval(start, end)
@validate_arguments_type_of_function()
def contains(self, maya_interval):
return (
self.start <= maya_interval.start and
self.end >= maya_interval.end
self.start <= maya_interval.start and self.end >= maya_interval.end
)
def __contains__(self, maya_dt):
@@ -559,8 +655,7 @@ class MayaInterval(object):
@validate_arguments_type_of_function()
def is_adjacent(self, maya_interval):
return (
self.start == maya_interval.end or
self.end == maya_interval.start
self.start == maya_interval.end or self.end == maya_interval.start
)
@property
@@ -577,14 +672,28 @@ class MayaInterval(object):
""".format(
self.start.datetime().strftime(ical_dt_format),
self.end.datetime().strftime(ical_dt_format),
).replace(' ', '').strip('\r\n').replace('\n', '\r\n')
).replace(
' ', ''
).strip(
'\r\n'
).replace(
'\n', '\r\n'
)
@staticmethod
def flatten(interval_list):
return functools.reduce(lambda reduced, maya_interval: (
(reduced[:-1] + maya_interval.combine(reduced[-1]))
if reduced else [maya_interval]
), sorted(interval_list), [])
return functools.reduce(
lambda reduced,
maya_interval: (
(
reduced[:-1] + maya_interval.combine(reduced[-1])
) if reduced else [
maya_interval
]
),
sorted(interval_list),
[],
)
@classmethod
def from_datetime(cls, start_dt=None, end_dt=None, duration=None):
@@ -599,7 +708,7 @@ def now():
return MayaDT(epoch=epoch)
def when(string, timezone='UTC', prefer_past=False):
def when(string, timezone='UTC', prefer_dates_from='current_period'):
""""Returns a MayaDT instance for the human moment specified.
Powered by dateparser. Useful for scraping websites.
@@ -610,16 +719,21 @@ def when(string, timezone='UTC', prefer_past=False):
Keyword Arguments:
string -- string to be parsed
timezone -- timezone referenced from (default: 'UTC')
prefer_past -- prefer parsing ambiguous date as in the past
prefer_dates_from -- what dates are prefered when `string` is ambigous.
options are 'past', 'future', and 'current_period'
(default: 'current_period'). see: [1]
Reference:
[1] dateparser.readthedocs.io/en/latest/usage.html#handling-incomplete-dates
"""
settings = {'TIMEZONE': timezone, 'RETURN_AS_TIMEZONE_AWARE': True, 'TO_TIMEZONE': 'UTC'}
if prefer_past:
settings['PREFER_DATES_FROM'] = 'past'
settings = {
'TIMEZONE': timezone,
'RETURN_AS_TIMEZONE_AWARE': True,
'TO_TIMEZONE': 'UTC',
'PREFER_DATES_FROM': prefer_dates_from,
}
dt = dateparser.parse(string, settings=settings)
if dt is None:
raise ValueError('invalid datetime input specified.')
@@ -629,21 +743,25 @@ def when(string, timezone='UTC', prefer_past=False):
def parse(string, timezone='UTC', day_first=False, year_first=True):
""""Returns a MayaDT instance for the machine-produced moment specified.
Powered by pendulum. Accepts most known formats. Useful for working with data.
Powered by pendulum.
Accepts most known formats. Useful for working with data.
Keyword Arguments:
string -- string to be parsed
timezone -- timezone referenced from (default: 'UTC')
day_first -- if true, the first value (e.g. 01/05/2016) is parsed as day.
if year_first is set to True, this distinguishes between YDM and YMD. (default: False)
year_first -- if true, the first value (e.g. 2016/05/01) is parsed as year (default: True)
day_first -- if true, the first value (e.g. 01/05/2016)
is parsed as day.
if year_first is set to True, this distinguishes
between YDM and YMD. (default: False)
year_first -- if true, the first value (e.g. 2016/05/01)
is parsed as year (default: True)
"""
options = {}
options['tz'] = timezone
options['day_first'] = day_first
options['year_first'] = year_first
dt = pendulum.parse(string, **options)
dt = pendulum.parse(str(string), **options)
return MayaDT.from_datetime(dt)
@@ -658,17 +776,32 @@ def _seconds_or_timedelta(duration):
elif isinstance(duration, timedelta):
dt_timedelta = duration
else:
raise TypeError('Expects argument as `datetime.timedelta` object '
'or seconds in `int` format')
raise TypeError(
'Expects argument as `datetime.timedelta` object '
'or seconds in `int` format'
)
return dt_timedelta
def _translate(dt, target_locale):
en = default_loader.get_locale("en")
target = default_loader.get_locale(target_locale)
naturaldate = humanize.naturaldate(dt)
base = en.translate(naturaldate, settings=dateparser.conf.settings)
return target.info["relative-type"][base][-1]
def intervals(start, end, interval):
"""Yields MayaDT objects between the start and end MayaDTs given, at a given interval (seconds or timedelta)."""
"""
Yields MayaDT objects between the start and end MayaDTs given,
at a given interval (seconds or timedelta).
"""
interval = _seconds_or_timedelta(interval)
current_timestamp = start
while current_timestamp.epoch < end.epoch:
yield current_timestamp
current_timestamp = current_timestamp.add(seconds=interval.seconds)
+46 -4
View File
@@ -1,11 +1,13 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import io
import os
import sys
import codecs
from shutil import rmtree
from setuptools import setup
from setuptools import find_packages, setup, Command
here = os.path.abspath(os.path.dirname(__file__))
@@ -21,16 +23,53 @@ if sys.argv[-1] == "publish":
required = [
'humanize',
'pytz',
'dateparser',
'ruamel.yaml',
'dateparser>=0.7.0',
'tzlocal',
'pendulum'
'pendulum>=1.0, <=1.5.1',
'snaptime'
]
packages = [
'maya',
]
class UploadCommand(Command):
"""Support setup.py upload."""
description = 'Build and publish the package.'
user_options = []
@staticmethod
def status(s):
"""Prints things in bold."""
print('\033[1m{0}\033[0m'.format(s))
def initialize_options(self):
pass
def finalize_options(self):
pass
def run(self):
try:
self.status('Removing previous builds…')
rmtree(os.path.join(here, 'dist'))
except OSError:
pass
self.status('Building Source and Wheel (universal) distribution…')
os.system('{0} setup.py sdist bdist_wheel --universal'.format(sys.executable))
self.status('Uploading the package to PyPi via Twine…')
os.system('twine upload dist/*')
self.status('Pushing git tags…')
os.system('git tag v{0}'.format(about['__version__']))
os.system('git push --tags')
sys.exit()
# About dict to store version and package info
about = dict()
with codecs.open(os.path.join(here, 'maya', '__version__.py'), 'r', encoding='utf-8') as f:
@@ -61,4 +100,7 @@ setup(
'Programming Language :: Python :: Implementation :: CPython',
'Topic :: Software Development :: Libraries :: Python Modules'
),
cmdclass={
'upload': UploadCommand,
},
)
+24
View File
@@ -0,0 +1,24 @@
from freezegun import freeze_time
import pytest
@pytest.fixture(params=[
("2018-03-25T00:00:00", 2),
("2018-03-25T01:00:00", 2),
("2018-03-25T02:00:00", 2),
("2018-03-25T02:30:00", 2),
("2018-03-25T03:00:00", 2),
("2018-03-25T04:00:00", 2),
("2018-10-28T00:00:00", 2),
("2018-10-28T01:00:00", 2),
("2018-10-28T02:00:00", 2),
("2018-10-28T02:30:00", 2),
("2018-10-28T03:00:00", 2),
("2018-10-28T04:00:00", 2),
], ids=lambda x: x[0] + "_off_" + str(x[1]))
def frozen_now(request):
now_string, tz_offset = request.param
with freeze_time(now_string, tz_offset=tz_offset):
yield
+184 -106
View File
@@ -1,83 +1,78 @@
import pytest
import pytz
import copy
import time
from datetime import date, timedelta, datetime as Datetime
import calendar
from datetime import timedelta, datetime as Datetime
import pytz
import pytest
import maya
from maya.core import _seconds_or_timedelta # import private function
def test_rfc2822():
r = maya.parse('February 21, 1994').rfc2822()
@pytest.mark.parametrize("string,expected", [
('February 21, 1994',
'Mon, 21 Feb 1994 00:00:00 GMT'),
])
def test_rfc2822(string, expected):
r = maya.parse(string).rfc2822()
d = maya.MayaDT.from_rfc2822(r)
assert r == 'Mon, 21 Feb 1994 00:00:00 GMT'
assert r == expected
assert r == d.rfc2822()
def test_iso8601():
r = maya.parse('February 21, 1994').iso8601()
@pytest.mark.parametrize("string,expected", [
('February 21, 1994',
'1994-02-21T00:00:00Z'),
])
def test_iso8601(string, expected):
r = maya.parse(string).iso8601()
d = maya.MayaDT.from_iso8601(r)
assert r == '1994-02-21T00:00:00Z'
assert r == expected
assert r == d.iso8601()
def test_parse_iso8601():
string = '20161001T1430.4+05:30'
expected = '2016-10-01T09:00:00.400000Z'
@pytest.mark.parametrize("string,expected", [
('20161001T1430.4+05:30',
'2016-10-01T09:00:00.400000Z'),
('2016T14',
'2016-01-01T14:00:00Z'),
('2016-10T14',
'2016-10-01T14:00:00Z'),
('2012W05',
'2012-01-30T00:00:00Z'),
('2012W055',
'2012-02-03T00:00:00Z'),
('2012007',
'2012-01-07T00:00:00Z'),
('2016-W07T09',
'2016-02-15T09:00:00Z'),
])
def test_parse_iso8601(string, expected):
d = maya.MayaDT.from_iso8601(string)
assert expected == d.iso8601()
string = '2016T14'
expected = '2016-01-01T14:00:00Z'
d = maya.MayaDT.from_iso8601(string)
assert expected == d.iso8601()
string = '2016-10T14'
expected = '2016-10-01T14:00:00Z'
d = maya.MayaDT.from_iso8601(string)
assert expected == d.iso8601()
string = '2012W05'
expected = '2012-01-30T00:00:00Z'
d = maya.MayaDT.from_iso8601(string)
assert expected == d.iso8601()
string = '2012W055'
expected = '2012-02-03T00:00:00Z'
d = maya.MayaDT.from_iso8601(string)
assert expected == d.iso8601()
string = '2012007'
expected = '2012-01-07T00:00:00Z'
d = maya.MayaDT.from_iso8601(string)
assert expected == d.iso8601()
string = '2016-W07T09'
expected = '2016-02-15T09:00:00Z'
d = maya.MayaDT.from_iso8601(string)
assert expected == d.iso8601()
@pytest.mark.usefixtures("frozen_now")
def test_struct():
now = round(time.time())
ts = time.gmtime(now)
m = maya.MayaDT.from_struct(ts)
dt = Datetime.fromtimestamp(now, pytz.UTC)
assert m._epoch != None
assert m._epoch is not None
assert m.datetime() == dt
ts = time.localtime(now)
m = maya.MayaDT.from_struct(ts)
dt = Datetime.fromtimestamp(time.mktime(ts) - maya.core.utc_offset(), pytz.UTC)
assert m._epoch != None
dt = Datetime.fromtimestamp(
time.mktime(ts) - maya.core.utc_offset(ts), pytz.UTC
)
assert m._epoch is not None
assert m.datetime() == dt
@@ -94,35 +89,33 @@ def test_issue_104():
def test_human_when():
r1 = maya.when('yesterday')
r2 = maya.when('today')
assert (r2.day - r1.day) in (1, -30, -29, -28, -27)
def test_machine_parse():
r1 = maya.parse('August 14, 2015')
assert r1.day == 14
r2 = maya.parse('August 15, 2015')
assert r2.day == 15
@pytest.mark.usefixtures("frozen_now")
def test_dt_tz_translation():
d1 = maya.now().datetime()
d2 = maya.now().datetime(to_timezone='EST')
assert (d1.hour - d2.hour) % 24 == 5
@pytest.mark.usefixtures("frozen_now")
def test_dt_tz_naive():
d1 = maya.now().datetime(naive=True)
assert d1.tzinfo is None
d2 = maya.now().datetime(to_timezone='EST', naive=True)
assert d2.tzinfo is None
assert (d1.hour - d2.hour) % 24 == 5
def test_random_date():
# Test properties for maya.when()
d1 = maya.when('11-17-11 08:09:10')
assert d1.year == 2011
@@ -134,7 +127,6 @@ def test_random_date():
assert d1.minute == 9
assert d1.second == 10
assert d1.microsecond == 0
# Test properties for maya.parse()
d2 = maya.parse('February 29, 1992 13:12:34')
assert d2.year == 1992
@@ -150,10 +142,8 @@ def test_random_date():
def test_print_date(capsys):
d = maya.when('11-17-11')
print(d)
out, err = capsys.readouterr()
assert out == 'Thu, 17 Nov 2011 00:00:00 GMT\n'
assert repr(d) == '<MayaDT epoch=1321488000.0>'
@@ -168,47 +158,90 @@ def test_slang_date():
assert d.slang_date() == 'tomorrow'
def test_slang_date_locale():
d = maya.when('tomorrow')
assert d.slang_date(locale='fr') == 'demain'
def test_slang_time():
d = maya.when('one hour ago')
assert d.slang_time() == 'an hour ago'
d = maya.when('1 hour ago')
assert d.slang_time() == '1 hour ago'
def test_parse():
d = maya.parse('February 21, 1994')
assert format(d) == '1994-02-21 00:00:00+00:00'
d = maya.parse('01/05/2016')
assert format(d) == '2016-01-05 00:00:00+00:00'
d = maya.parse('01/05/2016', day_first=True)
assert format(d) == '2016-05-01 00:00:00+00:00'
d = maya.parse('2016/05/01', year_first=True, day_first=False)
assert format(d) == '2016-05-01 00:00:00+00:00'
d = maya.parse('2016/01/05', year_first=True, day_first=True)
assert format(d) == '2016-05-01 00:00:00+00:00'
d = maya.parse('01/05/2016', timezone='UTC')
assert format(d) == '2016-01-05 00:00:00+00:00'
d = maya.parse('01/05/2016', timezone='US/Central')
assert format(d) == '2016-01-05 06:00:00+00:00'
def test_slang_time_locale():
d = maya.when('1 hour ago')
assert d.slang_time(locale='de') == 'vor 1 Stunde'
@pytest.mark.parametrize("string,kwds,expected", [
('February 21, 1994', {},
'1994-02-21 00:00:00+00:00'),
('01/05/2016', {},
'2016-01-05 00:00:00+00:00'),
('01/05/2016', dict(day_first=True),
'2016-05-01 00:00:00+00:00'),
('2016/05/01', dict(year_first=True, day_first=False),
'2016-05-01 00:00:00+00:00'),
('2016/01/05', dict(year_first=True, day_first=True),
'2016-05-01 00:00:00+00:00'),
('01/05/2016', dict(timezone='UTC'),
'2016-01-05 00:00:00+00:00'),
('01/05/2016', dict(timezone='US/Central'),
'2016-01-05 06:00:00+00:00'),
])
def test_parse(string, kwds, expected):
d = maya.parse(string, **kwds)
assert format(d) == expected
@pytest.mark.usefixtures("frozen_now")
def test_when_past():
next_month = str(maya.now().add(months=1).month)
this_year = maya.now().year
last_year = this_year - 1
two_days_away = maya.now().add(days=2)
future_date = maya.when(next_month)
past_date = maya.when(next_month, prefer_past=True)
past_date = maya.when(
two_days_away.slang_date(),
prefer_dates_from='past')
assert future_date.year == this_year
if next_month == '1':
assert past_date.year == this_year
else:
assert past_date.year == last_year
assert past_date < maya.now()
@pytest.mark.usefixtures("frozen_now")
def test_when_future():
two_days_away = maya.now().add(days=2)
future_date = maya.when(
two_days_away.slang_date(),
prefer_dates_from='future')
assert future_date > maya.now()
@pytest.mark.usefixtures("frozen_now")
def test_when_past_day_name():
two_days_away = maya.now().add(days=2)
past_date = maya.when(
calendar.day_name[two_days_away.weekday],
prefer_dates_from='past')
assert past_date < maya.now()
@pytest.mark.usefixtures("frozen_now")
def test_when_future_day_name():
two_days_away = maya.now().add(days=2)
future_date = maya.when(
calendar.day_name[two_days_away.weekday],
prefer_dates_from='future')
assert future_date > maya.now()
def test_datetime_to_timezone():
@@ -216,36 +249,37 @@ def test_datetime_to_timezone():
assert dt.tzinfo.zone == 'US/Eastern'
def test_rfc3339():
mdt = maya.when('2016-01-01')
def test_rfc3339_epoch():
mdt = maya.when('2016-01-01')
out = mdt.rfc3339()
mdt2 = maya.MayaDT.from_rfc3339(out)
assert mdt.epoch == mdt2.epoch
def test_rfc3339_format():
rfc3339 = maya.MayaDT.rfc3339(maya.when('2016-01-01T12:03:03Z'))
# it's important that the string has got a "max 1-digit millis" fragment
# as per https://tools.ietf.org/html/rfc3339#section-5.6
assert rfc3339 == '2016-01-01T12:03:03.0Z'
@pytest.mark.usefixtures("frozen_now")
def test_comparison_operations():
now = maya.now()
now_copy = copy.deepcopy(now)
tomorrow = maya.when('tomorrow')
assert (now == now_copy) is True
assert (now == tomorrow) is False
assert (now != now_copy) is False
assert (now != tomorrow) is True
assert (now < now_copy) is False
assert (now < tomorrow) is True
assert (now <= now_copy) is True
assert (now <= tomorrow) is True
assert (now > now_copy) is False
assert (now > tomorrow) is False
assert (now >= now_copy) is True
assert (now >= tomorrow) is False
# Check Exceptions
with pytest.raises(TypeError):
now == 1
@@ -271,46 +305,90 @@ def test_seconds_or_timedelta():
_seconds_or_timedelta('invalid interval')
@pytest.mark.usefixtures("frozen_now")
def test_intervals():
now = maya.now()
tomorrow = now.add(days=1)
assert len(list(maya.intervals(now, tomorrow, 60*60))) == 24
assert len(list(maya.intervals(now, tomorrow, 60 * 60))) == 24
@pytest.mark.usefixtures("frozen_now")
def test_dunder_add():
now = maya.now()
assert now + 1 == now.add(seconds=1)
assert now + timedelta(seconds=1) == now.add(seconds=1)
@pytest.mark.usefixtures("frozen_now")
def test_dunder_radd():
now = maya.now()
assert now.add(seconds=1) == now + 1
assert now.add(seconds=1) == now + timedelta(seconds=1)
@pytest.mark.usefixtures("frozen_now")
def test_dunder_sub():
now = maya.now()
assert now - 1 == now.subtract(seconds=1)
assert now - timedelta(seconds=1) == now.subtract(seconds=1)
@pytest.mark.usefixtures("frozen_now")
def test_mayaDT_sub():
now = maya.now()
then = now.add(days=1)
assert then - now == timedelta(24*60*60)
assert now - then == timedelta(-24*60*60)
assert then - now == timedelta(seconds=24 * 60 * 60)
assert now - then == timedelta(seconds=-24 * 60 * 60)
def test_core_local_timezone(monkeypatch):
@property
def mock_local_tz(self):
class StaticTzInfo(object):
zone = 'local'
def __repr__(self):
return "<StaticTzInfo 'local'>"
return StaticTzInfo()
monkeypatch.setattr(maya.MayaDT, '_local_tz', mock_local_tz)
mdt = maya.MayaDT(0)
assert mdt.local_timezone == 'UTC'
def test_getting_datetime_for_local_timezone(monkeypatch):
@property
def mock_local_tz(self):
class StaticTzInfo(object):
zone = 'Europe/Zurich'
def __repr__(self):
return "<StaticTzInfo 'Europe/Zurich'>"
return StaticTzInfo()
monkeypatch.setattr(maya.MayaDT, '_local_tz', mock_local_tz)
d = maya.parse('1994-02-21T12:00:00+05:30')
dt = pytz.timezone('Europe/Zurich').localize(
Datetime(1994, 2, 21, 7, 30))
assert d.local_datetime() == dt
@pytest.mark.parametrize("when_str,snap_str,expected_when", [
('Mon, 21 Feb 1994 21:21:42 GMT', '@d',
'Mon, 21 Feb 1994 00:00:00 GMT'),
])
def test_snaptime(when_str, snap_str, expected_when):
# given
dt = maya.when(when_str)
# when
dt = dt.snap(snap_str)
# then
assert dt == maya.when(expected_when)
+196 -214
View File
@@ -15,26 +15,20 @@ Melbourne = pytz.timezone('Australia/Melbourne')
def test_interval_requires_2_of_start_end_duration():
start = maya.now()
end = start.add(hours=1)
with pytest.raises(ValueError):
maya.MayaInterval(start=start)
with pytest.raises(ValueError):
maya.MayaInterval(end=end)
with pytest.raises(ValueError):
maya.MayaInterval(duration=60)
with pytest.raises(ValueError):
maya.MayaInterval(start=start, end=end, duration=60)
maya.MayaInterval(start=start, end=end)
maya.MayaInterval(start=start, duration=60)
maya.MayaInterval(end=end, duration=60)
def test_interval_requires_end_time_after_or_on_start_time():
with pytest.raises(ValueError):
maya.MayaInterval(start=maya.now(), duration=0)
maya.MayaInterval(start=maya.now(), duration=-1)
@@ -64,7 +58,9 @@ def test_interval_init_end_duration():
assert interval.start == end.subtract(seconds=duration)
@pytest.mark.parametrize('start_doy1,end_doy1,start_doy2,end_doy2,intersection_doys', (
@pytest.mark.parametrize(
'start_doy1,end_doy1,start_doy2,end_doy2,intersection_doys',
(
(0, 2, 1, 3, (1, 2)),
(0, 2, 3, 4, None),
(0, 2, 2, 3, None),
@@ -76,7 +72,8 @@ def test_interval_init_end_duration():
(1, 3, 1, 1, (1, 1)),
(2, 3, 1, 1, None),
(1, 3, 2, 2, (2, 2)),
), ids=(
),
ids=(
'overlapping',
'non-overlapping',
'adjacent',
@@ -87,21 +84,19 @@ def test_interval_init_end_duration():
'instant overlapping',
'instant overlapping start only (left)',
'instant disjoint (left)',
'instant overlapping (left)'
))
'instant overlapping (left)',
),
)
def test_interval_intersection(
start_doy1, end_doy1, start_doy2, end_doy2, intersection_doys
start_doy1, end_doy1, start_doy2, end_doy2, intersection_doys
):
base = maya.MayaDT.from_datetime(datetime(2016, 1, 1))
interval1 = maya.MayaInterval(
base.add(days=start_doy1),
base.add(days=end_doy1),
base.add(days=start_doy1), base.add(days=end_doy1)
)
interval2 = maya.MayaInterval(
base.add(days=start_doy2),
base.add(days=end_doy2),
base.add(days=start_doy2), base.add(days=end_doy2)
)
if intersection_doys:
start_doy_intersection, end_doy_intersection = intersection_doys
assert interval1 & interval2 == maya.MayaInterval(
@@ -110,7 +105,6 @@ def test_interval_intersection(
)
else:
assert (interval1 & interval2) is None
# check invalid argument
with pytest.raises(TypeError):
interval1 & 'invalid type'
@@ -119,13 +113,10 @@ def test_interval_intersection(
def test_interval_intersects():
base = maya.MayaDT.from_datetime(datetime(2016, 1, 1))
interval = maya.MayaInterval(base, base.add(days=1))
assert interval.intersects(interval)
assert not interval.intersects(maya.MayaInterval(
base.add(days=2),
base.add(days=3),
))
assert not interval.intersects(
maya.MayaInterval(base.add(days=2), base.add(days=3))
)
# check invalid argument
with pytest.raises(TypeError):
interval.intersects('invalid type')
@@ -135,13 +126,11 @@ def test_and_operator():
base = maya.MayaDT.from_datetime(datetime(2016, 1, 1))
interval1 = maya.MayaInterval(base, base.add(days=2))
interval2 = maya.MayaInterval(base.add(days=1), base.add(days=3))
assert (
interval1 & interval2 ==
interval2 & interval1 ==
interval1.intersection(interval2)
)
# check invalid argument
with pytest.raises(TypeError):
interval1.intersection('invalid type')
@@ -151,14 +140,11 @@ def test_interval_eq_operator():
start = maya.now()
end = start.add(hours=1)
interval = maya.MayaInterval(start=start, end=end)
assert interval == maya.MayaInterval(start=start, end=end)
assert interval != maya.MayaInterval(start=start, end=end.add(days=1))
# check invalid argument
with pytest.raises(TypeError):
interval == 'invalid type'
with pytest.raises(TypeError):
interval != 'invalid type'
@@ -167,7 +153,6 @@ def test_interval_timedelta():
start = maya.now()
delta = timedelta(hours=1)
interval = maya.MayaInterval(start=start, duration=delta)
assert interval.timedelta == delta
@@ -175,69 +160,55 @@ def test_interval_duration():
start = maya.now()
delta = timedelta(hours=1)
interval = maya.MayaInterval(start=start, duration=delta)
assert interval.duration == delta.total_seconds()
@pytest.mark.parametrize('start_doy1,end_doy1,start_doy2,end_doy2,expected', (
@pytest.mark.parametrize(
'start_doy1,end_doy1,start_doy2,end_doy2,expected',
(
(0, 2, 1, 3, False),
(0, 2, 3, 4, False),
(0, 2, 2, 3, False),
(0, 1, 0, 1, True),
(0, 3, 1, 2, True),
), ids=(
'overlapping',
'non-overlapping',
'adjacent',
'equal',
'subset',
))
),
ids=('overlapping', 'non-overlapping', 'adjacent', 'equal', 'subset'),
)
def test_interval_contains(
start_doy1, end_doy1, start_doy2, end_doy2, expected
start_doy1, end_doy1, start_doy2, end_doy2, expected
):
base = maya.MayaDT.from_datetime(datetime(2016, 1, 1))
interval1 = maya.MayaInterval(
base.add(days=start_doy1),
base.add(days=end_doy1),
base.add(days=start_doy1), base.add(days=end_doy1)
)
interval2 = maya.MayaInterval(
base.add(days=start_doy2),
base.add(days=end_doy2),
base.add(days=start_doy2), base.add(days=end_doy2)
)
assert interval1.contains(interval2) is expected
assert (interval2 in interval1) is expected
# check invalid argument
with pytest.raises(TypeError):
interval1.contains('invalid type')
@pytest.mark.parametrize('start_doy,end_doy,dt_doy,expected', (
@pytest.mark.parametrize(
'start_doy,end_doy,dt_doy,expected',
(
(2, 4, 1, False),
(2, 4, 2, True),
(2, 4, 3, True),
(2, 4, 4, False),
(2, 4, 5, False),
), ids=(
'before-start',
'on-start',
'during',
'on-end',
'after-end',
))
def test_interval_in_operator_maya_dt(
start_doy, end_doy, dt_doy, expected
):
),
ids=('before-start', 'on-start', 'during', 'on-end', 'after-end'),
)
def test_interval_in_operator_maya_dt(start_doy, end_doy, dt_doy, expected):
base = maya.MayaDT.from_datetime(datetime(2016, 1, 1))
interval = maya.MayaInterval(
start=base.add(days=start_doy),
end=base.add(days=end_doy),
start=base.add(days=start_doy), end=base.add(days=end_doy)
)
dt = base.add(days=dt_doy)
assert (dt in interval) is expected
# check invalid argument
with pytest.raises(TypeError):
'invalid type' in interval
@@ -247,91 +218,84 @@ def test_interval_hash():
start = maya.now()
end = start.add(hours=1)
interval = maya.MayaInterval(start=start, end=end)
assert hash(interval) == hash(maya.MayaInterval(start=start, end=end))
assert hash(interval) != hash(maya.MayaInterval(
start=start, end=end.add(days=1)))
assert hash(interval) != hash(
maya.MayaInterval(start=start, end=end.add(days=1))
)
def test_interval_iter():
start = maya.now()
end = start.add(days=1)
assert tuple(maya.MayaInterval(start=start, end=end)) == (start, end)
@pytest.mark.parametrize('start1,end1,start2,end2,expected', [
(1, 2, 1, 2, 0),
(1, 3, 2, 4, -1),
(2, 4, 1, 3, 1),
(1, 2, 1, 3, -1),
], ids=(
@pytest.mark.parametrize(
'start1,end1,start2,end2,expected',
[(1, 2, 1, 2, 0), (1, 3, 2, 4, -1), (2, 4, 1, 3, 1), (1, 2, 1, 3, -1)],
ids=(
'equal',
'less-than',
'greater-than',
'use-end-time-if-start-time-identical',
))
),
)
def test_interval_cmp(start1, end1, start2, end2, expected):
base = maya.now()
interval1 = maya.MayaInterval(
start=base.add(days=start1),
end=base.add(days=end1),
start=base.add(days=start1), end=base.add(days=end1)
)
interval2 = maya.MayaInterval(
start=base.add(days=start2),
end=base.add(days=end2),
start=base.add(days=start2), end=base.add(days=end2)
)
assert cmp(interval1, interval2) == expected
# check invalid argument
with pytest.raises(TypeError):
cmp(interval1, 'invalid type')
@pytest.mark.parametrize('start1,end1,start2,end2,expected', [
(1, 2, 2, 3, [(1, 3)]),
(1, 3, 2, 4, [(1, 4)]),
(1, 2, 3, 4, [(1, 2), (3, 4)]),
(1, 5, 2, 3, [(1, 5)]),
], ids=(
'adjacent',
'overlapping',
'non-overlapping',
'contains',
))
@pytest.mark.parametrize(
'start1,end1,start2,end2,expected',
[
(1, 2, 2, 3, [(1, 3)]),
(1, 3, 2, 4, [(1, 4)]),
(1, 2, 3, 4, [(1, 2), (3, 4)]),
(1, 5, 2, 3, [(1, 5)]),
],
ids=('adjacent', 'overlapping', 'non-overlapping', 'contains'),
)
def test_interval_combine(start1, end1, start2, end2, expected):
base = maya.now()
interval1 = maya.MayaInterval(
start=base.add(days=start1),
end=base.add(days=end1),
start=base.add(days=start1), end=base.add(days=end1)
)
interval2 = maya.MayaInterval(
start=base.add(days=start2),
end=base.add(days=end2),
start=base.add(days=start2), end=base.add(days=end2)
)
expected_intervals = [maya.MayaInterval(
start=base.add(days=start),
end=base.add(days=end),
) for start, end in expected]
expected_intervals = [
maya.MayaInterval(start=base.add(days=start), end=base.add(days=end))
for start, end in expected
]
assert interval1.combine(interval2) == expected_intervals
assert interval2.combine(interval1) == expected_intervals
# check invalid argument
with pytest.raises(TypeError):
interval2.combine('invalid type')
@pytest.mark.parametrize('start1,end1,start2,end2,expected', [
(1, 2, 3, 4, [(1, 2)]),
(1, 2, 2, 4, [(1, 2)]),
(2, 3, 1, 4, []),
(1, 4, 2, 3, [(1, 2), (3, 4)]),
(1, 4, 0, 2, [(2, 4)]),
(1, 4, 3, 5, [(1, 3)]),
(1, 4, 1, 2, [(2, 4)]),
(1, 4, 3, 4, [(1, 3)]),
], ids=(
@pytest.mark.parametrize(
'start1,end1,start2,end2,expected',
[
(1, 2, 3, 4, [(1, 2)]),
(1, 2, 2, 4, [(1, 2)]),
(2, 3, 1, 4, []),
(1, 4, 2, 3, [(1, 2), (3, 4)]),
(1, 4, 0, 2, [(2, 4)]),
(1, 4, 3, 5, [(1, 3)]),
(1, 4, 1, 2, [(2, 4)]),
(1, 4, 3, 4, [(1, 3)]),
],
ids=(
'non-overlapping',
'adjacent',
'contains',
@@ -340,115 +304,109 @@ def test_interval_combine(start1, end1, start2, end2, expected):
'overlaps-right',
'overlaps-left-identical-start',
'overlaps-right-identical-end',
))
),
)
def test_interval_subtract(start1, end1, start2, end2, expected):
base = maya.now()
interval1 = maya.MayaInterval(
start=base.add(days=start1),
end=base.add(days=end1),
start=base.add(days=start1), end=base.add(days=end1)
)
interval2 = maya.MayaInterval(
start=base.add(days=start2),
end=base.add(days=end2),
start=base.add(days=start2), end=base.add(days=end2)
)
expected_intervals = [maya.MayaInterval(
start=base.add(days=start),
end=base.add(days=end),
) for start, end in expected]
expected_intervals = [
maya.MayaInterval(start=base.add(days=start), end=base.add(days=end))
for start, end in expected
]
assert interval1.subtract(interval2) == expected_intervals
# check invalid argument
with pytest.raises(TypeError):
interval1.subtract('invalid type')
@pytest.mark.parametrize('start1,end1,start2,end2,expected', [
(1, 2, 2, 3, True),
(2, 3, 1, 2, True),
(1, 3, 2, 3, False),
(2, 3, 4, 5, False),
], ids=(
'adjacent-right',
'adjacent-left',
'overlapping',
'non-overlapping',
))
@pytest.mark.parametrize(
'start1,end1,start2,end2,expected',
[
(1, 2, 2, 3, True),
(2, 3, 1, 2, True),
(1, 3, 2, 3, False),
(2, 3, 4, 5, False),
],
ids=('adjacent-right', 'adjacent-left', 'overlapping', 'non-overlapping'),
)
def test_interval_is_adjacent(start1, end1, start2, end2, expected):
base = maya.now()
interval1 = maya.MayaInterval(
start=base.add(days=start1),
end=base.add(days=end1),
start=base.add(days=start1), end=base.add(days=end1)
)
interval2 = maya.MayaInterval(
start=base.add(days=start2),
end=base.add(days=end2),
start=base.add(days=start2), end=base.add(days=end2)
)
assert interval1.is_adjacent(interval2) == expected
# check invalid argument
with pytest.raises(TypeError):
interval1.is_adjacent('invalid type')
@pytest.mark.parametrize('start,end,delta,include_remainder,expected', [
(0, 10, 5, False, [(0, 5), (5, 10)]),
(0, 10, 5, True, [(0, 5), (5, 10)]),
(0, 10, 3, False, [(0, 3), (3, 6), (6, 9)]),
(0, 10, 3, True, [(0, 3), (3, 6), (6, 9), (9, 10)]),
(0, 2, 5, False, []),
(0, 2, 5, True, [(0, 2)]),
], ids=(
@pytest.mark.parametrize(
'start,end,delta,include_remainder,expected',
[
(0, 10, 5, False, [(0, 5), (5, 10)]),
(0, 10, 5, True, [(0, 5), (5, 10)]),
(0, 10, 3, False, [(0, 3), (3, 6), (6, 9)]),
(0, 10, 3, True, [(0, 3), (3, 6), (6, 9), (9, 10)]),
(0, 2, 5, False, []),
(0, 2, 5, True, [(0, 2)]),
],
ids=(
'even-split',
'even-split-include-partial',
'uneven-split-do-not-include-partial',
'uneven-split-include-partial',
'delta-larger-than-timepsan-do-not-include-partial',
'delta-larger-than-timepsan-include-partial',
))
),
)
def test_interval_split(start, end, delta, include_remainder, expected):
base = maya.now()
interval = maya.MayaInterval(
start=base.add(days=start),
end=base.add(days=end),
start=base.add(days=start), end=base.add(days=end)
)
delta = timedelta(days=delta)
expected_intervals = [
maya.MayaInterval(
start=base.add(days=s),
end=base.add(days=e),
) for s, e in expected
maya.MayaInterval(start=base.add(days=s), end=base.add(days=e))
for s, e in expected
]
assert expected_intervals == list(interval.split(
delta, include_remainder=include_remainder))
assert expected_intervals == list(
interval.split(delta, include_remainder=include_remainder)
)
def test_interval_split_non_positive_delta():
start = maya.now()
end = start.add(days=1)
interval = maya.MayaInterval(start=start, end=end)
with pytest.raises(ValueError):
list(interval.split(timedelta(seconds=0)))
with pytest.raises(ValueError):
list(interval.split(timedelta(seconds=-10)))
@pytest.mark.parametrize('start,end,minutes,timezone,snap_out,expected_start,expected_end', [
((5, 12), (8, 48), 30, None, False, (5, 30), (8, 30)),
((5, 12), (8, 48), 30, None, True, (5, 0), (9, 0)),
((5, 15), (9, 0), 15, None, False, (5, 15), (9, 0)),
((5, 15), (9, 0), 15, None, True, (5, 15), (9, 0)),
((6, 50), (9, 15), 60, 'America/New_York', False, (7, 0), (9, 0)),
((6, 50), (9, 15), 60, 'America/New_York', True, (6, 0), (10, 0)),
((6, 20), (6, 50), 60, None, False, (6, 0), (6, 0)),
((6, 20), (6, 50), 60, None, True, (6, 0), (7, 0)),
((6, 20), (6, 50), 60, 'America/Chicago', False, (6, 0), (6, 0)),
((6, 20), (6, 50), 60, 'America/Chicago', True, (6, 0), (7, 0)),
], ids=(
@pytest.mark.parametrize(
'start,end,minutes,timezone,snap_out,expected_start,expected_end',
[
((5, 12), (8, 48), 30, None, False, (5, 30), (8, 30)),
((5, 12), (8, 48), 30, None, True, (5, 0), (9, 0)),
((5, 15), (9, 0), 15, None, False, (5, 15), (9, 0)),
((5, 15), (9, 0), 15, None, True, (5, 15), (9, 0)),
((6, 50), (9, 15), 60, 'America/New_York', False, (7, 0), (9, 0)),
((6, 50), (9, 15), 60, 'America/New_York', True, (6, 0), (10, 0)),
((6, 20), (6, 50), 60, None, False, (6, 0), (6, 0)),
((6, 20), (6, 50), 60, None, True, (6, 0), (7, 0)),
((6, 20), (6, 50), 60, 'America/Chicago', False, (6, 0), (6, 0)),
((6, 20), (6, 50), 60, 'America/Chicago', True, (6, 0), (7, 0)),
],
ids=(
'normal',
'normal-snap_out',
'already-quantized',
@@ -459,21 +417,20 @@ def test_interval_split_non_positive_delta():
'too-small-snap_out',
'too-small-with-timezone',
'too-small-with-timezone-snap_out',
))
def test_quantize(start, end, minutes, timezone, snap_out, expected_start, expected_end):
),
)
def test_quantize(
start, end, minutes, timezone, snap_out, expected_start, expected_end
):
base = maya.MayaDT.from_datetime(datetime(2017, 1, 1))
interval = maya.MayaInterval(
start=base.add(hours=start[0], minutes=start[1]),
end=base.add(hours=end[0], minutes=end[1]),
)
kwargs = {'timezone': timezone} if timezone is not None else {}
quantized_interval = interval.quantize(
timedelta(minutes=minutes),
snap_out=snap_out,
**kwargs
timedelta(minutes=minutes), snap_out=snap_out, **kwargs
)
assert quantized_interval == maya.MayaInterval(
start=base.add(hours=expected_start[0], minutes=expected_start[1]),
end=base.add(hours=expected_end[0], minutes=expected_end[1]),
@@ -484,7 +441,6 @@ def test_quantize_invalid_delta():
start = maya.now()
end = start.add(days=1)
interval = maya.MayaInterval(start=start, end=end)
with pytest.raises(ValueError):
interval.quantize(timedelta(minutes=0))
with pytest.raises(ValueError):
@@ -495,12 +451,13 @@ def test_interval_flatten_non_overlapping():
step = 2
max_hour = 20
base = maya.now()
intervals = [maya.MayaInterval(
start=base.add(hours=hour),
duration=timedelta(hours=step - 1),
) for hour in range(0, max_hour, step)]
intervals = [
maya.MayaInterval(
start=base.add(hours=hour), duration=timedelta(hours=step - 1)
)
for hour in range(0, max_hour, step)
]
random.shuffle(intervals)
assert maya.MayaInterval.flatten(intervals) == sorted(intervals)
@@ -508,35 +465,35 @@ def test_interval_flatten_adjacent():
step = 2
max_hour = 20
base = maya.when('jan/1/2011')
intervals = [
maya.MayaInterval(
start=base.add(hours=hour),
duration=timedelta(hours=step),
) for hour in range(0, max_hour, step)
start=base.add(hours=hour), duration=timedelta(hours=step)
)
for hour in range(0, max_hour, step)
]
random.shuffle(intervals)
assert maya.MayaInterval.flatten(intervals) == [maya.MayaInterval(
start=base,
duration=timedelta(hours=max_hour),
)]
assert maya.MayaInterval.flatten(intervals) == [
maya.MayaInterval(start=base, duration=timedelta(hours=max_hour))
]
def test_interval_flatten_intersecting():
step = 2
max_hour = 20
base = maya.now()
intervals = [maya.MayaInterval(
start=base.add(hours=hour),
duration=timedelta(hours=step, minutes=30),
) for hour in range(0, max_hour, step)]
intervals = [
maya.MayaInterval(
start=base.add(hours=hour),
duration=timedelta(hours=step, minutes=30),
)
for hour in range(0, max_hour, step)
]
random.shuffle(intervals)
assert maya.MayaInterval.flatten(intervals) == [maya.MayaInterval(
start=base,
duration=timedelta(hours=max_hour, minutes=30),
)]
assert maya.MayaInterval.flatten(intervals) == [
maya.MayaInterval(
start=base, duration=timedelta(hours=max_hour, minutes=30)
)
]
def test_interval_flatten_containing():
@@ -544,16 +501,16 @@ def test_interval_flatten_containing():
max_hour = 20
base = maya.now()
containing_interval = maya.MayaInterval(
start=base,
end=base.add(hours=max_hour + step),
start=base, end=base.add(hours=max_hour + step)
)
intervals = [maya.MayaInterval(
start=base.add(hours=hour),
duration=timedelta(hours=step - 1),
) for hour in range(2, max_hour, step)]
intervals = [
maya.MayaInterval(
start=base.add(hours=hour), duration=timedelta(hours=step - 1)
)
for hour in range(2, max_hour, step)
]
intervals.append(containing_interval)
random.shuffle(intervals)
assert maya.MayaInterval.flatten(intervals) == [containing_interval]
@@ -561,24 +518,18 @@ def test_interval_from_datetime():
start = maya.now()
duration = timedelta(hours=1)
end = start + duration
interval = maya.MayaInterval.from_datetime(
start_dt=start.datetime(naive=False),
end_dt=end.datetime(naive=False),
start_dt=start.datetime(naive=False), end_dt=end.datetime(naive=False)
)
assert interval.start == start
assert interval.end == end
interval2 = maya.MayaInterval.from_datetime(
start_dt=start.datetime(naive=False),
duration=duration,
start_dt=start.datetime(naive=False), duration=duration
)
assert interval2.start == start
assert interval2.end == end
interval3 = maya.MayaInterval.from_datetime(
end_dt=end.datetime(naive=False),
duration=duration,
end_dt=end.datetime(naive=False), duration=duration
)
assert interval3.start == start
assert interval3.end == end
@@ -588,3 +539,34 @@ def test_interval_iso8601():
start = maya.when('11-17-11 08:09:10')
interval = maya.MayaInterval(start=start, duration=1)
assert interval.iso8601() == '2011-11-17T08:09:10Z/2011-11-17T08:09:11Z'
def test_interval_from_iso8601():
interval = maya.MayaInterval.from_iso8601(
"2018-03-18T14:27:18Z/2018-04-01T04:15:27Z"
)
s = maya.when("2018-03-18T14:27:18Z")
e = maya.when("2018-04-01T04:15:27Z")
assert interval.start == s
assert interval.end == e
def test_interval_from_iso8601_duration():
interval = maya.MayaInterval.from_iso8601(
"2018-03-18T14:27:18Z/P13DT13H48M9S"
)
s = maya.when("2018-03-18T14:27:18Z")
e = maya.when("2018-04-01T04:15:27Z")
assert interval.start == s
assert interval.end == e
interval = maya.MayaInterval.from_iso8601(
"2018-03-05T14:27:18Z/P2W"
)
s = maya.when("2018-03-05T14:27:18Z")
e = maya.when("2018-03-19T14:27:18Z")
assert interval.start == s
assert interval.end == e
+14
View File
@@ -0,0 +1,14 @@
[tox]
envlist = py27, py34, py35, py36
[testenv]
deps = pipenv
commands=
pipenv install --dev --skip-lock
pytest tests/ {posargs}
[testenv:py27]
basepython = python2.7
deps =
{[testenv]deps}
funcsigs