mirror of
https://github.com/kennethreitz/maya.git
synced 2026-06-05 23:00:18 +00:00
Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 04a82f3078 | |||
| 5f6b5fc66d | |||
| 15a5c9eedb | |||
| 7c5d5871d3 | |||
| b411d5e6d1 | |||
| 5458b0bf15 | |||
| 02f91de523 | |||
| 00d174c9e2 | |||
| f87f705a53 | |||
| 9a850b4212 | |||
| 84a5f700c3 | |||
| 13e9f2d896 | |||
| 0c206ad0db | |||
| aa87723a0c | |||
| d51a6a57ec | |||
| 3cb65f227c |
@@ -2,8 +2,14 @@ language: python
|
|||||||
python:
|
python:
|
||||||
- "2.7"
|
- "2.7"
|
||||||
- "3.6"
|
- "3.6"
|
||||||
|
- "3.7-dev"
|
||||||
|
|
||||||
|
matrix:
|
||||||
|
allow_failures:
|
||||||
|
- python: "3.7-dev"
|
||||||
|
|
||||||
# command to install dependencies
|
# command to install dependencies
|
||||||
install: pip install pipenv; pipenv lock; pipenv install --dev
|
install: pip install pipenv; pipenv lock; pipenv install --dev
|
||||||
|
|
||||||
# command to run tests
|
# command to run tests
|
||||||
script: pipenv run pytest tests/
|
script: pipenv run pytest tests/
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
.PHONY: tests docs
|
||||||
|
|
||||||
tests:
|
tests:
|
||||||
pytest tests/
|
pytest tests/
|
||||||
docs:
|
docs:
|
||||||
|
|||||||
@@ -1,11 +1,15 @@
|
|||||||
[dev-packages]
|
[dev-packages]
|
||||||
|
|
||||||
pytest = "*"
|
pytest = "*"
|
||||||
Sphinx = "*"
|
sphinx = "*"
|
||||||
|
|
||||||
|
|
||||||
[packages]
|
[packages]
|
||||||
|
|
||||||
humanize = "*"
|
humanize = "*"
|
||||||
pytz = "*"
|
pytz = "*"
|
||||||
dateparser = "*"
|
dateparser = "*"
|
||||||
"ruamel.yaml" = "*"
|
"ruamel.yaml" = "*"
|
||||||
tzlocal = "*"
|
tzlocal = "*"
|
||||||
pendulum = ">=1.0"
|
pendulum = ">=1.0"
|
||||||
|
snaptime = "*"
|
||||||
|
|||||||
Generated
+129
-107
@@ -1,20 +1,7 @@
|
|||||||
{
|
{
|
||||||
"_meta": {
|
"_meta": {
|
||||||
"hash": {
|
"hash": {
|
||||||
"sha256": "e7c76d32bf7d24c6e3637eb39049c1543ba59729fe50da193414bde84a120587"
|
"sha256": "3abd33d2f9beea083faca673fa3864252623b284fa24f9a8b939aed12428318c"
|
||||||
},
|
|
||||||
"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,
|
"pipfile-spec": 6,
|
||||||
"requires": {},
|
"requires": {},
|
||||||
@@ -29,111 +16,124 @@
|
|||||||
"default": {
|
"default": {
|
||||||
"dateparser": {
|
"dateparser": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:e2629d2f8361722c6047138ca085256c9f2cf5cc657fd66122aa0564afa4dc33",
|
"sha256:940828183c937bcec530753211b70f673c0a9aab831e43273489b310538dff86",
|
||||||
"sha256:f8c24317120b06f71691d28076764ec084a132be2a250a78fdf54f6b427cac95"
|
"sha256:b452ef8b36cd78ae86a50721794bc674aa3994e19b570f7ba92810f4e0a2ae03"
|
||||||
],
|
],
|
||||||
"version": "==0.6.0"
|
"index": "pypi",
|
||||||
|
"version": "==0.7.0"
|
||||||
},
|
},
|
||||||
"humanize": {
|
"humanize": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:a43f57115831ac7c70de098e6ac46ac13be00d69abbf60bdcac251344785bb19"
|
"sha256:a43f57115831ac7c70de098e6ac46ac13be00d69abbf60bdcac251344785bb19"
|
||||||
],
|
],
|
||||||
|
"index": "pypi",
|
||||||
"version": "==0.5.1"
|
"version": "==0.5.1"
|
||||||
},
|
},
|
||||||
"pendulum": {
|
"pendulum": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:9196f0aa4eec534aaf02b45c47dccc6f74a255ecdab6c772cb6bcea6b22790e4",
|
"sha256:0c14388546db6605a860b8b7112cb69d0b11c9ce5e072210504544e0d4575799",
|
||||||
"sha256:a34690d8d4fc8eab34ea2dd9a99482dbbf0b1f059fe25effe204dd59bceda069",
|
"sha256:39a255776528afe11ea0d57814f9bf3729c1e0b99063af2e5c6cfd750c3e1f7f",
|
||||||
"sha256:1577a44b1f4bbc942136bce654df24e31735e1ff6aaa37e0a6207abf13868be9",
|
"sha256:3c85e8cbc91f45e1cc916cc9180b34153cd6aaaaacfb51a48b3156318314fa82",
|
||||||
"sha256:f68f0f13498c9350ffc712765c4e0bdc824a4afd767d1a71933ff7be380bf75e",
|
"sha256:8199206c479b13947dcac63c025575d035331bb3819d1783dc1d568a11962906",
|
||||||
"sha256:76d6861664126fef4cbbdc6218ca09d81c4ed8da4c6df9637e0069f7d820f901",
|
"sha256:8798aeca58b3dd7ffdc5a4993c9eaafedc4048165429e8f499ddd62c73bf3964",
|
||||||
"sha256:327c89477e6ea0e240cd9f94c241747a534ac6f3e71c9b2f3298485ffc8939b2",
|
"sha256:881efe37328de0785c0731d462e1485a45712f2cd5cb55907d6c15458460ebeb",
|
||||||
"sha256:4f1675010fd934aea011642c33c0dd9bc6954d9be7032c7f9ccfea1ac8d752d7",
|
"sha256:bcca072f82e84b419efec1320cd3ee5c230d263f3a601b146651ed4db77d89f0",
|
||||||
"sha256:e996c34fb101c9c6d88a839c19af74d7c067b92ed3371274efcf4d4b6dc160a6"
|
"sha256:ff0c5fa3af4a471a218408c448b804ac6bccb105127727474f4e83c0e4072e97"
|
||||||
],
|
],
|
||||||
"version": "==1.4.0"
|
"index": "pypi",
|
||||||
|
"version": "==1.4.2"
|
||||||
},
|
},
|
||||||
"python-dateutil": {
|
"python-dateutil": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:95511bae634d69bc7329ba55e646499a842bc4ec342ad54a8cdb65645a0aad3c",
|
"sha256:07009062406cffd554a9b4135cd2ff167c9bf6b7aac61fe946c93e69fad1bbd8",
|
||||||
"sha256:891c38b2a02f5bb1be3e4793866c8df49c7d19baabf9c1bad62547e0b4866aca"
|
"sha256:8f95bb7e6edbb2456a51a1fb58c8dca942024b4f5844cae62c90aa88afe6e300"
|
||||||
],
|
],
|
||||||
"version": "==2.6.1"
|
"version": "==2.7.0"
|
||||||
},
|
},
|
||||||
"pytz": {
|
"pytz": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:80af0f3008046b9975242012a985f04c5df1f01eed4ec1633d56cc47a75a6a48",
|
"sha256:07edfc3d4d2705a20a6e99d97f0c4b61c800b8232dc1c04d87e8554f130148dd",
|
||||||
"sha256:feb2365914948b8620347784b6b6da356f31c9d03560259070b2f30cff3d469d",
|
"sha256:3a47ff71597f821cd84a162e71593004286e5be07a340fd462f0d33a760782b5",
|
||||||
"sha256:59707844a9825589878236ff2f4e0dc9958511b7ffaae94dc615da07d4a68d33",
|
"sha256:410bcd1d6409026fbaa65d9ed33bf6dd8b1e94a499e32168acfc7b332e4095c0",
|
||||||
"sha256:d0ef5ef55ed3d37854320d4926b04a4cb42a2e88f71da9ddfdacfde8e364f027",
|
"sha256:5bd55c744e6feaa4d599a6cbd8228b4f8f9ba96de2c38d56f08e534b3c9edf0d",
|
||||||
"sha256:c41c62827ce9cafacd6f2f7018e4f83a6f1986e87bfd000b8cfbd4ab5da95f1a",
|
"sha256:61242a9abc626379574a166dc0e96a66cd7c3b27fc10868003fa210be4bff1c9",
|
||||||
"sha256:8cc90340159b5d7ced6f2ba77694d946fc975b09f1a51d93f3ce3bb399396f94",
|
"sha256:887ab5e5b32e4d0c86efddd3d055c1f363cbaa583beb8da5e22d2fa2f64d51ef",
|
||||||
"sha256:dd2e4ca6ce3785c8dd342d1853dd9052b19290d5bf66060846e5dc6b8d6667f7",
|
"sha256:ba18e6a243b3625513d85239b3e49055a2f0318466e0b8a92b8fb8ca7ccdf55f",
|
||||||
"sha256:699d18a2a56f19ee5698ab1123bbcc1d269d061996aeb1eda6d89248d3542b82",
|
"sha256:ed6509d9af298b7995d69a440e2822288f2eca1681b8cce37673dbb10091e5fe",
|
||||||
"sha256:fae4cffc040921b8a2d60c6cf0b5d662c1190fe54d718271db4eb17d44a185b7"
|
"sha256:f93ddcdd6342f94cea379c73cddb5724e0d6d0a1c91c9bdef364dc0368ba4fda"
|
||||||
],
|
],
|
||||||
"version": "==2017.3"
|
"index": "pypi",
|
||||||
|
"version": "==2018.3"
|
||||||
},
|
},
|
||||||
"pytzdata": {
|
"pytzdata": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:cd5b72400a7378b3b45eef5929cbe97ed44c3368685c35c477e316ebaa7e1809",
|
"sha256:4e2cceb54335cd6c28caea46b15cd592e2aec5e8b05b0241cbccfb1b23c02ae7",
|
||||||
"sha256:e87376f2ee7cb89af5ddea5ed07ce3e98a55f891d07ae87d8c49e99f069423f2"
|
"sha256:7cd949123e2c2060fd12793de3a4a449e36b5dea5e169b810a3ac3f0b9877cfa"
|
||||||
],
|
],
|
||||||
"version": "==2017.3.1"
|
"version": "==2018.3"
|
||||||
},
|
},
|
||||||
"regex": {
|
"regex": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:28a542117efd479cff110711c28f34cb40636f670d427dc42710b5183fbd92c4",
|
"sha256:1b428a296531ea1642a7da48562746309c5c06471a97bd0c02dd6a82e9cecee8",
|
||||||
"sha256:93064faef899911a7378443fcfb1f2e93e19706a324162fc71dd996c85b24f37",
|
"sha256:27d72bb42dffb32516c28d218bb054ce128afd3e18464f30837166346758af67",
|
||||||
"sha256:9c4b23f8a68470522a0cdfbdcffbe2f61d7659b5d505cb7c1a729a6805345210",
|
"sha256:32cf4743debee9ea12d3626ee21eae83052763740e04086304e7a74778bf58c9",
|
||||||
"sha256:45fed2f98e5ca0fc420ff31cbd9eccbee41e4d607569fddaba0faea36ef6043d",
|
"sha256:32f6408dbca35040bc65f9f4ae1444d5546411fde989cb71443a182dd643305e",
|
||||||
"sha256:079c656874ba6c2e3332df488d4eff33b94b4e260e3901dce1553cb4f1f36c73",
|
"sha256:333687d9a44738c486735955993f83bd22061a416c48f5a5f9e765e90cf1b0c9",
|
||||||
"sha256:c1ef8f72944bef49261daaa83c7d77439f54f8deefefc0e2cf7144ac8b20ac1f",
|
"sha256:35eeccf17af3b017a54d754e160af597036435c58eceae60f1dd1364ae1250c7",
|
||||||
"sha256:26795107acb81bb07ef832832548834b03846eb046e5ba9a5917ee012c68c1e6",
|
"sha256:361a1fd703a35580a4714ec28d85e29780081a4c399a99bbfb2aee695d72aedb",
|
||||||
"sha256:3e2609e0e366b21c3db3d41159d99e7bd37e02caaba24bba77ec5cc594c62c4e",
|
"sha256:494bed6396a20d3aa6376bdf2d3fbb1005b8f4339558d8ac7b53256755f80303",
|
||||||
"sha256:801e054c1aa163545d29e186a1bea779437a19b49c4da9e11049624c2d2bfd31",
|
"sha256:5b9c0ddd5b4afa08c9074170a2ea9b34ea296e32aeea522faaaaeeeb2fe0af2e",
|
||||||
"sha256:51306abdaac9e712b208066d284ddc7e3a332c77ad6054ba8d305d607609a328",
|
"sha256:a50532f61b23d4ab9d216a6214f359dd05c911c1a1ad20986b6738a782926c1a",
|
||||||
"sha256:660990e223ef2f71cb78b4e106a9a023652a31fd305051a901b0f87171b69e24",
|
"sha256:a9243d7b359b72c681a2c32eaa7ace8d346b7e8ce09d172a683acf6853161d9c",
|
||||||
"sha256:47230e1af3479810b1ef2bf23768b5195588a03eea6248b678d6893c48f58082",
|
"sha256:b44624a38d07d3c954c84ad302c29f7930f4bf01443beef5589e9157b14e2a29",
|
||||||
"sha256:5f15a27c24ed4ad2ed492abe80ddf27d35d63ef6c0d8878c915de6bf9f36c6f3",
|
"sha256:be42a601aaaeb7a317f818490a39d153952a97c40c6e9beeb2a1103616405348",
|
||||||
"sha256:67025161b70b0625749b1b89200da59fecc2fda9d1e46f9ef588f9a4af9fc48f",
|
"sha256:eee4d94b1a626490fc8170ffd788883f8c641b576e11ba9b4a29c9f6623371e0",
|
||||||
"sha256:139678fc013b75e486e580c39b4c52d085ed7362e400960f8be1711a414f16b5"
|
"sha256:f69d1201a4750f763971ea8364ed95ee888fc128968b39d38883a72a4d005895"
|
||||||
],
|
],
|
||||||
"version": "==2018.1.10"
|
"version": "==2018.2.21"
|
||||||
},
|
},
|
||||||
"ruamel.yaml": {
|
"ruamel.yaml": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:14d161558e3bf89e87d77c218098be22fa9a0d6d0bea40250fce525b1d0cbee2",
|
|
||||||
"sha256:fcfc24a21594c071cc4588e84b7657a1f47ebcf6037c6c43fa15c4bbd3989ec2",
|
|
||||||
"sha256:02babffd019911841ba01b76e23dfec7c9e9b2725503fb2698c4982fa1a6e835",
|
|
||||||
"sha256:c0908896e34b617ead40552cab03c1769bdc43d1da02419160dc900c5dfddde2",
|
|
||||||
"sha256:01e30ecb1b1c0ebf9fce814dc20dace402571517277799291202b61b22096c24",
|
"sha256:01e30ecb1b1c0ebf9fce814dc20dace402571517277799291202b61b22096c24",
|
||||||
"sha256:b6c5d5f03ba78e3f27c7188a00c4e09b6a4507fe3154ba40a294e09cb30ee016",
|
"sha256:02babffd019911841ba01b76e23dfec7c9e9b2725503fb2698c4982fa1a6e835",
|
||||||
"sha256:9225c83952d28f302cfc23c3d9a6f8231bfd581476d7aff1e3c7de49eecb4ee9",
|
|
||||||
"sha256:c41e04b526d0153c9246cfab87d7ddefdc9f165cb8886a8ec48ba7a2b73069f6",
|
|
||||||
"sha256:6d05c5a5baf829c70916c226ef3200650846a7227de226bca8a59efaf88bb973",
|
|
||||||
"sha256:e3bbfe0d294e08fdbb0cb05485435a2ceb4e168e98b5dc611f051c1864986b4b",
|
|
||||||
"sha256:68c8f2986bcb91b6db1aea8698941769840c7257e951a9377048f7eff35be773",
|
|
||||||
"sha256:072f6364a89972e8dc0afdce3335a709d5464dfeaa4f736d092a54574338b874",
|
"sha256:072f6364a89972e8dc0afdce3335a709d5464dfeaa4f736d092a54574338b874",
|
||||||
|
"sha256:14d161558e3bf89e87d77c218098be22fa9a0d6d0bea40250fce525b1d0cbee2",
|
||||||
"sha256:5504398fc755a2b14c9983b2101161a8591a4b30812590cc1c365e7fcc117dfa",
|
"sha256:5504398fc755a2b14c9983b2101161a8591a4b30812590cc1c365e7fcc117dfa",
|
||||||
"sha256:e2d2715bf92156bec5fb42e92e95dac1c4d9904f8a3d4e2d0c438758fe9092d7",
|
"sha256:68c8f2986bcb91b6db1aea8698941769840c7257e951a9377048f7eff35be773",
|
||||||
|
"sha256:6d05c5a5baf829c70916c226ef3200650846a7227de226bca8a59efaf88bb973",
|
||||||
"sha256:6d7929b24e329d662fa43b657fddfee5260e2d35d0a543065cd755d4e17a9b2f",
|
"sha256:6d7929b24e329d662fa43b657fddfee5260e2d35d0a543065cd755d4e17a9b2f",
|
||||||
|
"sha256:8dc74821e4bb6b21fb1ab35964e159391d99ee44981d07d57bf96e2395f3ef75",
|
||||||
|
"sha256:9225c83952d28f302cfc23c3d9a6f8231bfd581476d7aff1e3c7de49eecb4ee9",
|
||||||
|
"sha256:b6c5d5f03ba78e3f27c7188a00c4e09b6a4507fe3154ba40a294e09cb30ee016",
|
||||||
|
"sha256:c0908896e34b617ead40552cab03c1769bdc43d1da02419160dc900c5dfddde2",
|
||||||
|
"sha256:c41e04b526d0153c9246cfab87d7ddefdc9f165cb8886a8ec48ba7a2b73069f6",
|
||||||
|
"sha256:e2d2715bf92156bec5fb42e92e95dac1c4d9904f8a3d4e2d0c438758fe9092d7",
|
||||||
|
"sha256:e3bbfe0d294e08fdbb0cb05485435a2ceb4e168e98b5dc611f051c1864986b4b",
|
||||||
"sha256:f2d02a4af5a13b09d0b823cdd0317b54f3e0115e50b5ac4d9840c3a1b566817f",
|
"sha256:f2d02a4af5a13b09d0b823cdd0317b54f3e0115e50b5ac4d9840c3a1b566817f",
|
||||||
"sha256:8dc74821e4bb6b21fb1ab35964e159391d99ee44981d07d57bf96e2395f3ef75"
|
"sha256:fcfc24a21594c071cc4588e84b7657a1f47ebcf6037c6c43fa15c4bbd3989ec2"
|
||||||
],
|
],
|
||||||
|
"index": "pypi",
|
||||||
"version": "==0.15.35"
|
"version": "==0.15.35"
|
||||||
},
|
},
|
||||||
"six": {
|
"six": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb",
|
"sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9",
|
||||||
"sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9"
|
"sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb"
|
||||||
],
|
],
|
||||||
"version": "==1.11.0"
|
"version": "==1.11.0"
|
||||||
},
|
},
|
||||||
|
"snaptime": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:e3f1eb89043d58d30721ab98cb65023f1a4c2740e3b197704298b163c92d508b"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==0.2.4"
|
||||||
|
},
|
||||||
"tzlocal": {
|
"tzlocal": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:4ebeb848845ac898da6519b9b31879cf13b6626f7184c496037b818e238f2c4e"
|
"sha256:4ebeb848845ac898da6519b9b31879cf13b6626f7184c496037b818e238f2c4e"
|
||||||
],
|
],
|
||||||
|
"index": "pypi",
|
||||||
"version": "==1.5.1"
|
"version": "==1.5.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -147,15 +147,15 @@
|
|||||||
},
|
},
|
||||||
"attrs": {
|
"attrs": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:a17a9573a6f475c99b551c0e0a812707ddda1ec9653bed04c13841404ed6f450",
|
"sha256:1c7960ccfd6a005cd9f7ba884e6316b5e430a3f1a6c37c5f87d8b43f83b54ec9",
|
||||||
"sha256:1c7960ccfd6a005cd9f7ba884e6316b5e430a3f1a6c37c5f87d8b43f83b54ec9"
|
"sha256:a17a9573a6f475c99b551c0e0a812707ddda1ec9653bed04c13841404ed6f450"
|
||||||
],
|
],
|
||||||
"version": "==17.4.0"
|
"version": "==17.4.0"
|
||||||
},
|
},
|
||||||
"babel": {
|
"babel": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:ad209a68d7162c4cff4b29cdebe3dec4cef75492df501b0049a9433c96ce6f80",
|
"sha256:8ce4cb6fdd4393edd323227cba3a077bceb2a6ce5201c902c65e730046f41f14",
|
||||||
"sha256:8ce4cb6fdd4393edd323227cba3a077bceb2a6ce5201c902c65e730046f41f14"
|
"sha256:ad209a68d7162c4cff4b29cdebe3dec4cef75492df501b0049a9433c96ce6f80"
|
||||||
],
|
],
|
||||||
"version": "==2.5.3"
|
"version": "==2.5.3"
|
||||||
},
|
},
|
||||||
@@ -168,32 +168,32 @@
|
|||||||
},
|
},
|
||||||
"chardet": {
|
"chardet": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691",
|
"sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae",
|
||||||
"sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae"
|
"sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"
|
||||||
],
|
],
|
||||||
"version": "==3.0.4"
|
"version": "==3.0.4"
|
||||||
},
|
},
|
||||||
"docutils": {
|
"docutils": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:7a4bd47eaf6596e1295ecb11361139febe29b084a87bf005bf899f9a42edc3c6",
|
|
||||||
"sha256:02aec4bd92ab067f6ff27a38a38a41173bf01bed8f89157768c1573f53e474a6",
|
"sha256:02aec4bd92ab067f6ff27a38a38a41173bf01bed8f89157768c1573f53e474a6",
|
||||||
"sha256:51e64ef2ebfb29cae1faa133b3710143496eca21c530f3f71424d77687764274"
|
"sha256:51e64ef2ebfb29cae1faa133b3710143496eca21c530f3f71424d77687764274",
|
||||||
|
"sha256:7a4bd47eaf6596e1295ecb11361139febe29b084a87bf005bf899f9a42edc3c6"
|
||||||
],
|
],
|
||||||
"version": "==0.14"
|
"version": "==0.14"
|
||||||
},
|
},
|
||||||
"idna": {
|
"idna": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:8c7309c718f94b3a625cb648ace320157ad16ff131ae0af362c9f21b80ef6ec4",
|
"sha256:2c6a5de3089009e3da7c5dde64a141dbc8551d5b7f6cf4ed7c2568d0cc520a8f",
|
||||||
"sha256:2c6a5de3089009e3da7c5dde64a141dbc8551d5b7f6cf4ed7c2568d0cc520a8f"
|
"sha256:8c7309c718f94b3a625cb648ace320157ad16ff131ae0af362c9f21b80ef6ec4"
|
||||||
],
|
],
|
||||||
"version": "==2.6"
|
"version": "==2.6"
|
||||||
},
|
},
|
||||||
"imagesize": {
|
"imagesize": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:6ebdc9e0ad188f9d1b2cdd9bc59cbe42bf931875e829e7a595e6b3abdc05cdfb",
|
"sha256:3620cc0cadba3f7475f9940d22431fc4d407269f1be59ec9b8edcca26440cf18",
|
||||||
"sha256:0ab2c62b87987e3252f89d30b7cedbec12a01af9274af9ffa48108f2c13c6062"
|
"sha256:5b326e4678b6925158ccc66a9fa3122b6106d7c876ee32d7de6ce59385b96315"
|
||||||
],
|
],
|
||||||
"version": "==0.7.1"
|
"version": "==1.0.0"
|
||||||
},
|
},
|
||||||
"jinja2": {
|
"jinja2": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
@@ -208,6 +208,13 @@
|
|||||||
],
|
],
|
||||||
"version": "==1.0"
|
"version": "==1.0"
|
||||||
},
|
},
|
||||||
|
"packaging": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:e9215d2d2535d3ae866c3d6efc77d5b24a0192cce0ff20e42896cc0664f889c0",
|
||||||
|
"sha256:f019b770dd64e585a99714f1fd5e01c7a8f11b45635aa953fd41c689a657375b"
|
||||||
|
],
|
||||||
|
"version": "==17.1"
|
||||||
|
},
|
||||||
"pluggy": {
|
"pluggy": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:7f8ae7f5bdf75671a718d2daf0a64b7885f74510bcd98b1a0bb420eb9a9d0cff"
|
"sha256:7f8ae7f5bdf75671a718d2daf0a64b7885f74510bcd98b1a0bb420eb9a9d0cff"
|
||||||
@@ -228,26 +235,40 @@
|
|||||||
],
|
],
|
||||||
"version": "==2.2.0"
|
"version": "==2.2.0"
|
||||||
},
|
},
|
||||||
|
"pyparsing": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:0832bcf47acd283788593e7a0f542407bd9550a55a8a8435214a1960e04bcb04",
|
||||||
|
"sha256:281683241b25fe9b80ec9d66017485f6deff1af5cde372469134b56ca8447a07",
|
||||||
|
"sha256:8f1e18d3fd36c6795bb7e02a39fd05c611ffc2596c1e0d995d34d67630426c18",
|
||||||
|
"sha256:9e8143a3e15c13713506886badd96ca4b579a87fbdf49e550dbfc057d6cb218e",
|
||||||
|
"sha256:b8b3117ed9bdf45e14dcc89345ce638ec7e0e29b2b579fa1ecf32ce45ebac8a5",
|
||||||
|
"sha256:e4d45427c6e20a59bf4f88c639dcc03ce30d193112047f94012102f235853a58",
|
||||||
|
"sha256:fee43f17a9c4087e7ed1605bd6df994c6173c1e977d7ade7b651292fab2bd010"
|
||||||
|
],
|
||||||
|
"version": "==2.2.0"
|
||||||
|
},
|
||||||
"pytest": {
|
"pytest": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:b84878865558194630c6147f44bdaef27222a9f153bbd4a08908b16bf285e0b1",
|
"sha256:062027955bccbc04d2fcd5d79690947e018ba31abe4c90b2c6721abec734261b",
|
||||||
"sha256:53548280ede7818f4dc2ad96608b9f08ae2cc2ca3874f2ceb6f97e3583f25bc4"
|
"sha256:117bad36c1a787e1a8a659df35de53ba05f9f3398fb9e4ac17e80ad5903eb8c5"
|
||||||
],
|
],
|
||||||
"version": "==3.3.2"
|
"index": "pypi",
|
||||||
|
"version": "==3.4.2"
|
||||||
},
|
},
|
||||||
"pytz": {
|
"pytz": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:80af0f3008046b9975242012a985f04c5df1f01eed4ec1633d56cc47a75a6a48",
|
"sha256:07edfc3d4d2705a20a6e99d97f0c4b61c800b8232dc1c04d87e8554f130148dd",
|
||||||
"sha256:feb2365914948b8620347784b6b6da356f31c9d03560259070b2f30cff3d469d",
|
"sha256:3a47ff71597f821cd84a162e71593004286e5be07a340fd462f0d33a760782b5",
|
||||||
"sha256:59707844a9825589878236ff2f4e0dc9958511b7ffaae94dc615da07d4a68d33",
|
"sha256:410bcd1d6409026fbaa65d9ed33bf6dd8b1e94a499e32168acfc7b332e4095c0",
|
||||||
"sha256:d0ef5ef55ed3d37854320d4926b04a4cb42a2e88f71da9ddfdacfde8e364f027",
|
"sha256:5bd55c744e6feaa4d599a6cbd8228b4f8f9ba96de2c38d56f08e534b3c9edf0d",
|
||||||
"sha256:c41c62827ce9cafacd6f2f7018e4f83a6f1986e87bfd000b8cfbd4ab5da95f1a",
|
"sha256:61242a9abc626379574a166dc0e96a66cd7c3b27fc10868003fa210be4bff1c9",
|
||||||
"sha256:8cc90340159b5d7ced6f2ba77694d946fc975b09f1a51d93f3ce3bb399396f94",
|
"sha256:887ab5e5b32e4d0c86efddd3d055c1f363cbaa583beb8da5e22d2fa2f64d51ef",
|
||||||
"sha256:dd2e4ca6ce3785c8dd342d1853dd9052b19290d5bf66060846e5dc6b8d6667f7",
|
"sha256:ba18e6a243b3625513d85239b3e49055a2f0318466e0b8a92b8fb8ca7ccdf55f",
|
||||||
"sha256:699d18a2a56f19ee5698ab1123bbcc1d269d061996aeb1eda6d89248d3542b82",
|
"sha256:ed6509d9af298b7995d69a440e2822288f2eca1681b8cce37673dbb10091e5fe",
|
||||||
"sha256:fae4cffc040921b8a2d60c6cf0b5d662c1190fe54d718271db4eb17d44a185b7"
|
"sha256:f93ddcdd6342f94cea379c73cddb5724e0d6d0a1c91c9bdef364dc0368ba4fda"
|
||||||
],
|
],
|
||||||
"version": "==2017.3"
|
"index": "pypi",
|
||||||
|
"version": "==2018.3"
|
||||||
},
|
},
|
||||||
"requests": {
|
"requests": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
@@ -258,29 +279,30 @@
|
|||||||
},
|
},
|
||||||
"six": {
|
"six": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb",
|
"sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9",
|
||||||
"sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9"
|
"sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb"
|
||||||
],
|
],
|
||||||
"version": "==1.11.0"
|
"version": "==1.11.0"
|
||||||
},
|
},
|
||||||
"snowballstemmer": {
|
"snowballstemmer": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:9f3bcd3c401c3e862ec0ebe6d2c069ebc012ce142cce209c098ccb5b09136e89",
|
"sha256:919f26a68b2c17a7634da993d91339e288964f93c274f1343e3bbbe2096e1128",
|
||||||
"sha256:919f26a68b2c17a7634da993d91339e288964f93c274f1343e3bbbe2096e1128"
|
"sha256:9f3bcd3c401c3e862ec0ebe6d2c069ebc012ce142cce209c098ccb5b09136e89"
|
||||||
],
|
],
|
||||||
"version": "==1.2.1"
|
"version": "==1.2.1"
|
||||||
},
|
},
|
||||||
"sphinx": {
|
"sphinx": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:b8baed19394af85b21755c68c7ec4eac57e8a482ed89cd01cd5d5ff72200fe0f",
|
"sha256:41ae26acc6130ccf6ed47e5cca73742b80d55a134f0ab897c479bba8d3640b8e",
|
||||||
"sha256:c39a6fa41bd3ec6fc10064329a664ed3a3ca2e27640a823dc520c682e4433cdb"
|
"sha256:da987de5fcca21a4acc7f67a86a363039e67ac3e8827161e61b91deb131c0ee8"
|
||||||
],
|
],
|
||||||
"version": "==1.6.6"
|
"index": "pypi",
|
||||||
|
"version": "==1.7.1"
|
||||||
},
|
},
|
||||||
"sphinxcontrib-websupport": {
|
"sphinxcontrib-websupport": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:f4932e95869599b89bf4f80fc3989132d83c9faa5bf633e7b5e0c25dffb75da2",
|
"sha256:7a85961326aa3a400cd4ad3c816d70ed6f7c740acd7ce5d78cd0a67825072eb9",
|
||||||
"sha256:7a85961326aa3a400cd4ad3c816d70ed6f7c740acd7ce5d78cd0a67825072eb9"
|
"sha256:f4932e95869599b89bf4f80fc3989132d83c9faa5bf633e7b5e0c25dffb75da2"
|
||||||
],
|
],
|
||||||
"version": "==1.0.1"
|
"version": "==1.0.1"
|
||||||
},
|
},
|
||||||
|
|||||||
+6
-1
@@ -88,10 +88,15 @@ Behold, datetimes for humans!
|
|||||||
>>> maya.intervals(start=maya.now(), end=maya.now().add(days=1), interval=60*60)
|
>>> maya.intervals(start=maya.now(), end=maya.now().add(days=1), interval=60*60)
|
||||||
<generator object intervals at 0x105ba5820>
|
<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
|
☤ 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:
|
For example:
|
||||||
|
|
||||||
|
|||||||
+11
-9
@@ -1,5 +1,4 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
"""
|
"""
|
||||||
maya.compat
|
maya.compat
|
||||||
~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~
|
||||||
@@ -12,24 +11,19 @@ import sys
|
|||||||
# -------
|
# -------
|
||||||
# Pythons
|
# Pythons
|
||||||
# -------
|
# -------
|
||||||
|
|
||||||
# Syntax sugar.
|
# Syntax sugar.
|
||||||
_ver = sys.version_info
|
_ver = sys.version_info
|
||||||
|
# : Python 2.x?
|
||||||
#: Python 2.x?
|
|
||||||
is_py2 = (_ver[0] == 2)
|
is_py2 = (_ver[0] == 2)
|
||||||
|
# : Python 3.x?
|
||||||
#: Python 3.x?
|
|
||||||
is_py3 = (_ver[0] == 3)
|
is_py3 = (_ver[0] == 3)
|
||||||
|
|
||||||
# ---------
|
# ---------
|
||||||
# Specifics
|
# Specifics
|
||||||
# ---------
|
# ---------
|
||||||
|
|
||||||
if is_py2:
|
if is_py2:
|
||||||
cmp = cmp
|
cmp = cmp
|
||||||
|
|
||||||
elif is_py3:
|
elif is_py3:
|
||||||
|
|
||||||
def cmp(a, b):
|
def cmp(a, b):
|
||||||
"""
|
"""
|
||||||
Compare two objects.
|
Compare two objects.
|
||||||
@@ -38,8 +32,10 @@ elif is_py3:
|
|||||||
"""
|
"""
|
||||||
if a < b:
|
if a < b:
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
elif a == b:
|
elif a == b:
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
else:
|
else:
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
@@ -59,36 +55,42 @@ def comparable(klass):
|
|||||||
c = self.__cmp__(other)
|
c = self.__cmp__(other)
|
||||||
if c is NotImplemented:
|
if c is NotImplemented:
|
||||||
return c
|
return c
|
||||||
|
|
||||||
return c == 0
|
return c == 0
|
||||||
|
|
||||||
def __ne__(self, other):
|
def __ne__(self, other):
|
||||||
c = self.__cmp__(other)
|
c = self.__cmp__(other)
|
||||||
if c is NotImplemented:
|
if c is NotImplemented:
|
||||||
return c
|
return c
|
||||||
|
|
||||||
return c != 0
|
return c != 0
|
||||||
|
|
||||||
def __lt__(self, other):
|
def __lt__(self, other):
|
||||||
c = self.__cmp__(other)
|
c = self.__cmp__(other)
|
||||||
if c is NotImplemented:
|
if c is NotImplemented:
|
||||||
return c
|
return c
|
||||||
|
|
||||||
return c < 0
|
return c < 0
|
||||||
|
|
||||||
def __le__(self, other):
|
def __le__(self, other):
|
||||||
c = self.__cmp__(other)
|
c = self.__cmp__(other)
|
||||||
if c is NotImplemented:
|
if c is NotImplemented:
|
||||||
return c
|
return c
|
||||||
|
|
||||||
return c <= 0
|
return c <= 0
|
||||||
|
|
||||||
def __gt__(self, other):
|
def __gt__(self, other):
|
||||||
c = self.__cmp__(other)
|
c = self.__cmp__(other)
|
||||||
if c is NotImplemented:
|
if c is NotImplemented:
|
||||||
return c
|
return c
|
||||||
|
|
||||||
return c > 0
|
return c > 0
|
||||||
|
|
||||||
def __ge__(self, other):
|
def __ge__(self, other):
|
||||||
c = self.__cmp__(other)
|
c = self.__cmp__(other)
|
||||||
if c is NotImplemented:
|
if c is NotImplemented:
|
||||||
return c
|
return c
|
||||||
|
|
||||||
return c >= 0
|
return c >= 0
|
||||||
|
|
||||||
klass.__lt__ = __lt__
|
klass.__lt__ = __lt__
|
||||||
|
|||||||
+198
-106
@@ -1,7 +1,6 @@
|
|||||||
# ___ __ ___ _ _ ___
|
# ___ __ ___ _ _ ___
|
||||||
# || \/ | ||=|| \\// ||=||
|
# || \/ | ||=|| \\// ||=||
|
||||||
# || | || || // || ||
|
# || | || || // || ||
|
||||||
|
|
||||||
# Ignore warnings for yaml usage.
|
# Ignore warnings for yaml usage.
|
||||||
import warnings
|
import warnings
|
||||||
import ruamel.yaml
|
import ruamel.yaml
|
||||||
@@ -10,19 +9,20 @@ warnings.simplefilter('ignore', ruamel.yaml.error.UnsafeLoaderWarning)
|
|||||||
|
|
||||||
import email.utils
|
import email.utils
|
||||||
import time
|
import time
|
||||||
|
import functools
|
||||||
from datetime import timedelta, datetime as Datetime
|
from datetime import timedelta, datetime as Datetime
|
||||||
|
|
||||||
import functools
|
import re
|
||||||
import pytz
|
import pytz
|
||||||
import humanize
|
import humanize
|
||||||
import dateparser
|
import dateparser
|
||||||
import pendulum
|
import pendulum
|
||||||
|
import snaptime
|
||||||
from tzlocal import get_localzone
|
from tzlocal import get_localzone
|
||||||
|
from dateutil.relativedelta import relativedelta
|
||||||
|
|
||||||
from .compat import cmp, comparable
|
from .compat import cmp, comparable
|
||||||
|
|
||||||
_EPOCH_START = (1970, 1, 1)
|
|
||||||
|
|
||||||
|
|
||||||
def validate_class_type_arguments(operator):
|
def validate_class_type_arguments(operator):
|
||||||
"""
|
"""
|
||||||
@@ -31,14 +31,20 @@ def validate_class_type_arguments(operator):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def inner(function):
|
def inner(function):
|
||||||
|
|
||||||
def wrapper(self, *args, **kwargs):
|
def wrapper(self, *args, **kwargs):
|
||||||
for arg in args + tuple(kwargs.values()):
|
for arg in args + tuple(kwargs.values()):
|
||||||
if not isinstance(arg, self.__class__):
|
if not isinstance(arg, self.__class__):
|
||||||
raise TypeError('unorderable types: {}() {} {}()'.format(
|
raise TypeError(
|
||||||
type(self).__name__, operator, type(arg).__name__))
|
'unorderable types: {}() {} {}()'.format(
|
||||||
|
type(self).__name__, operator, type(arg).__name__
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
return function(self, *args, **kwargs)
|
return function(self, *args, **kwargs)
|
||||||
|
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
return inner
|
return inner
|
||||||
|
|
||||||
|
|
||||||
@@ -51,26 +57,34 @@ def validate_arguments_type_of_function(param_type=None):
|
|||||||
|
|
||||||
Note: Use this decorator on the functions of the class.
|
Note: Use this decorator on the functions of the class.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def inner(function):
|
def inner(function):
|
||||||
|
|
||||||
def wrapper(self, *args, **kwargs):
|
def wrapper(self, *args, **kwargs):
|
||||||
type_ = param_type or type(self)
|
type_ = param_type or type(self)
|
||||||
for arg in args + tuple(kwargs.values()):
|
for arg in args + tuple(kwargs.values()):
|
||||||
if not isinstance(arg, type_):
|
if not isinstance(arg, type_):
|
||||||
raise TypeError(('Invalid Type: {}.{}() accepts only the '
|
raise TypeError(
|
||||||
'arguments of type "<{}>"').format(
|
(
|
||||||
type(self).__name__,
|
'Invalid Type: {}.{}() accepts only the '
|
||||||
function.__name__,
|
'arguments of type "<{}>"'
|
||||||
type_.__name__,
|
).format(
|
||||||
)
|
type(self).__name__,
|
||||||
)
|
function.__name__,
|
||||||
|
type_.__name__,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
return function(self, *args, **kwargs)
|
return function(self, *args, **kwargs)
|
||||||
|
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
return inner
|
return inner
|
||||||
|
|
||||||
|
|
||||||
class MayaDT(object):
|
class MayaDT(object):
|
||||||
"""The Maya Datetime object."""
|
"""The Maya Datetime object."""
|
||||||
|
__EPOCH_START = (1970, 1, 1)
|
||||||
|
|
||||||
def __init__(self, epoch):
|
def __init__(self, epoch):
|
||||||
super(MayaDT, self).__init__()
|
super(MayaDT, self).__init__()
|
||||||
@@ -114,7 +128,9 @@ class MayaDT(object):
|
|||||||
return hash(int(self.epoch))
|
return hash(int(self.epoch))
|
||||||
|
|
||||||
def __add__(self, duration):
|
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):
|
def __radd__(self, duration):
|
||||||
return self + duration
|
return self + duration
|
||||||
@@ -122,25 +138,39 @@ class MayaDT(object):
|
|||||||
def __sub__(self, duration_or_date):
|
def __sub__(self, duration_or_date):
|
||||||
if isinstance(duration_or_date, MayaDT):
|
if isinstance(duration_or_date, MayaDT):
|
||||||
return self.subtract_date(dt=duration_or_date)
|
return self.subtract_date(dt=duration_or_date)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
return self.subtract(
|
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):
|
def add(self, **kwargs):
|
||||||
"""Returns a new MayaDT object with the given offsets."""
|
"""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):
|
def subtract(self, **kwargs):
|
||||||
"""Returns a new MayaDT object with the given offsets."""
|
"""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):
|
def subtract_date(self, **kwargs):
|
||||||
"""Returns a timedelta object with the duration between the dates"""
|
"""Returns a timedelta object with the duration between the dates"""
|
||||||
return timedelta(self.epoch - kwargs['dt'].epoch)
|
return timedelta(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
|
# Timezone Crap
|
||||||
# -------------
|
# -------------
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def timezone(self):
|
def timezone(self):
|
||||||
"""Returns the UTC tzinfo name. It's always UTC. Always."""
|
"""Returns the UTC tzinfo name. It's always UTC. Always."""
|
||||||
@@ -153,9 +183,10 @@ class MayaDT(object):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def local_timezone(self):
|
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:
|
if self._local_tz.zone in pytz.all_timezones:
|
||||||
return self._local_tz.zone
|
return self._local_tz.zone
|
||||||
|
|
||||||
return self.timezone
|
return self.timezone
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -167,17 +198,14 @@ class MayaDT(object):
|
|||||||
@validate_arguments_type_of_function(Datetime)
|
@validate_arguments_type_of_function(Datetime)
|
||||||
def __dt_to_epoch(dt):
|
def __dt_to_epoch(dt):
|
||||||
"""Converts a datetime into an epoch."""
|
"""Converts a datetime into an epoch."""
|
||||||
|
|
||||||
# Assume UTC if no datetime is provided.
|
# Assume UTC if no datetime is provided.
|
||||||
if dt.tzinfo is None:
|
if dt.tzinfo is None:
|
||||||
dt = dt.replace(tzinfo=pytz.utc)
|
dt = dt.replace(tzinfo=pytz.utc)
|
||||||
|
epoch_start = Datetime(*MayaDT.__EPOCH_START, tzinfo=pytz.timezone('UTC'))
|
||||||
epoch_start = Datetime(*_EPOCH_START, tzinfo=pytz.timezone('UTC'))
|
|
||||||
return (dt - epoch_start).total_seconds()
|
return (dt - epoch_start).total_seconds()
|
||||||
|
|
||||||
# Importers
|
# Importers
|
||||||
# ---------
|
# ---------
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@validate_arguments_type_of_function(Datetime)
|
@validate_arguments_type_of_function(Datetime)
|
||||||
def from_datetime(klass, dt):
|
def from_datetime(klass, dt):
|
||||||
@@ -187,8 +215,11 @@ class MayaDT(object):
|
|||||||
@classmethod
|
@classmethod
|
||||||
@validate_arguments_type_of_function(time.struct_time)
|
@validate_arguments_type_of_function(time.struct_time)
|
||||||
def from_struct(klass, struct, timezone=pytz.UTC):
|
def from_struct(klass, struct, timezone=pytz.UTC):
|
||||||
"""Returns MayaDT instance from a 9-tuple struct (assumed to be from gmtime())"""
|
"""Returns MayaDT instance from a 9-tuple struct
|
||||||
struct_time = time.mktime(struct) - utc_offset()
|
|
||||||
|
It's assumed to be from gmtime().
|
||||||
|
"""
|
||||||
|
struct_time = time.mktime(struct) - utc_offset(struct)
|
||||||
dt = Datetime.fromtimestamp(struct_time, timezone)
|
dt = Datetime.fromtimestamp(struct_time, timezone)
|
||||||
return klass(klass.__dt_to_epoch(dt))
|
return klass(klass.__dt_to_epoch(dt))
|
||||||
|
|
||||||
@@ -209,28 +240,27 @@ class MayaDT(object):
|
|||||||
|
|
||||||
# Exporters
|
# Exporters
|
||||||
# ---------
|
# ---------
|
||||||
|
|
||||||
def datetime(self, to_timezone=None, naive=False):
|
def datetime(self, to_timezone=None, naive=False):
|
||||||
"""Returns a timezone-aware datetime...
|
"""Returns a timezone-aware datetime...
|
||||||
Defaulting to UTC (as it should).
|
Defaulting to UTC (as it should).
|
||||||
|
|
||||||
Keyword Arguments:
|
Keyword Arguments:
|
||||||
to_timezone {string} -- timezone to convert to (default: None/UTC)
|
to_timezone {str} -- timezone to convert to (default: None/UTC)
|
||||||
naive {boolean} -- if True, the tzinfo is simply dropped (default: False)
|
naive {bool} -- if True,
|
||||||
|
the tzinfo is simply dropped (default: False)
|
||||||
"""
|
"""
|
||||||
if to_timezone:
|
if to_timezone:
|
||||||
dt = self.datetime().astimezone(pytz.timezone(to_timezone))
|
dt = self.datetime().astimezone(pytz.timezone(to_timezone))
|
||||||
else:
|
else:
|
||||||
dt = Datetime.utcfromtimestamp(self._epoch)
|
dt = Datetime.utcfromtimestamp(self._epoch)
|
||||||
dt.replace(tzinfo=self._tz)
|
dt.replace(tzinfo=self._tz)
|
||||||
|
|
||||||
# Strip the timezone info if requested to do so.
|
# Strip the timezone info if requested to do so.
|
||||||
if naive:
|
if naive:
|
||||||
return dt.replace(tzinfo=None)
|
return dt.replace(tzinfo=None)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
if dt.tzinfo is None:
|
if dt.tzinfo is None:
|
||||||
dt = dt.replace(tzinfo=self._tz)
|
dt = dt.replace(tzinfo=self._tz)
|
||||||
|
|
||||||
return dt
|
return dt
|
||||||
|
|
||||||
def iso8601(self):
|
def iso8601(self):
|
||||||
@@ -249,7 +279,6 @@ class MayaDT(object):
|
|||||||
|
|
||||||
# Properties
|
# Properties
|
||||||
# ----------
|
# ----------
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def year(self):
|
def year(self):
|
||||||
return self.datetime().year
|
return self.datetime().year
|
||||||
@@ -272,7 +301,10 @@ class MayaDT(object):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def weekday(self):
|
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()
|
return self.datetime().isoweekday()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -297,7 +329,6 @@ class MayaDT(object):
|
|||||||
|
|
||||||
# Human Slang Extras
|
# Human Slang Extras
|
||||||
# ------------------
|
# ------------------
|
||||||
|
|
||||||
def slang_date(self):
|
def slang_date(self):
|
||||||
""""Returns human slang representation of date."""
|
""""Returns human slang representation of date."""
|
||||||
dt = self.datetime(naive=True, to_timezone=self.local_timezone)
|
dt = self.datetime(naive=True, to_timezone=self.local_timezone)
|
||||||
@@ -309,9 +340,20 @@ class MayaDT(object):
|
|||||||
return humanize.naturaltime(dt)
|
return humanize.naturaltime(dt)
|
||||||
|
|
||||||
|
|
||||||
def utc_offset():
|
def utc_offset(time_struct=None):
|
||||||
"""Returns the current local time offset from UTC accounting for DST """
|
"""
|
||||||
ts = time.localtime()
|
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]:
|
if ts[-1]:
|
||||||
offset = time.altzone
|
offset = time.altzone
|
||||||
else:
|
else:
|
||||||
@@ -322,12 +364,14 @@ def utc_offset():
|
|||||||
def to_utc_offset_naive(dt):
|
def to_utc_offset_naive(dt):
|
||||||
if dt.tzinfo is None:
|
if dt.tzinfo is None:
|
||||||
return dt
|
return dt
|
||||||
|
|
||||||
return dt.astimezone(pytz.utc).replace(tzinfo=None)
|
return dt.astimezone(pytz.utc).replace(tzinfo=None)
|
||||||
|
|
||||||
|
|
||||||
def to_utc_offset_aware(dt):
|
def to_utc_offset_aware(dt):
|
||||||
if dt.tzinfo is not None:
|
if dt.tzinfo is not None:
|
||||||
return dt
|
return dt
|
||||||
|
|
||||||
return pytz.utc.localize(dt)
|
return pytz.utc.localize(dt)
|
||||||
|
|
||||||
|
|
||||||
@@ -336,40 +380,46 @@ def to_iso8601(dt):
|
|||||||
|
|
||||||
|
|
||||||
def end_of_day_midnight(dt):
|
def end_of_day_midnight(dt):
|
||||||
return dt if dt.time() == time.min else \
|
if dt.time() == time.min:
|
||||||
(dt.replace(hour=0, minute=0, second=0, microsecond=0) + timedelta(days=1))
|
return dt
|
||||||
|
|
||||||
|
else:
|
||||||
|
return (
|
||||||
|
dt.replace(hour=0, minute=0, second=0, microsecond=0) +
|
||||||
|
timedelta(days=1)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@comparable
|
@comparable
|
||||||
class MayaInterval(object):
|
class MayaInterval(object):
|
||||||
"""
|
"""
|
||||||
A MayaInterval represents a range between two datetimes, inclusive of the start
|
A MayaInterval represents a range between two datetimes,
|
||||||
and exclusive of the end.
|
inclusive of the start and exclusive of the end.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, start=None, end=None, duration=None):
|
def __init__(self, start=None, end=None, duration=None):
|
||||||
try:
|
try:
|
||||||
# Ensure that proper arguments were passed.
|
# Ensure that proper arguments were passed.
|
||||||
assert any((
|
assert any(
|
||||||
(start and end),
|
(
|
||||||
(start and duration is not None),
|
(start and end),
|
||||||
(end and duration is not None),
|
(start and duration is not None),
|
||||||
))
|
(end and duration is not None),
|
||||||
|
)
|
||||||
|
)
|
||||||
assert not all((start, end, duration is not None))
|
assert not all((start, end, duration is not None))
|
||||||
except AssertionError:
|
except AssertionError:
|
||||||
raise ValueError(
|
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.
|
# Convert duration to timedelta if seconds were provided.
|
||||||
if duration:
|
if duration:
|
||||||
duration = _seconds_or_timedelta(duration)
|
duration = _seconds_or_timedelta(duration)
|
||||||
|
|
||||||
if not start:
|
if not start:
|
||||||
start = end - duration
|
start = end - duration
|
||||||
|
|
||||||
if not end:
|
if not end:
|
||||||
end = start + duration
|
end = start + duration
|
||||||
|
|
||||||
if start > end:
|
if start > end:
|
||||||
raise ValueError('MayaInterval cannot end before it starts')
|
raise ValueError('MayaInterval cannot end before it starts')
|
||||||
|
|
||||||
@@ -377,28 +427,56 @@ class MayaInterval(object):
|
|||||||
self.end = end
|
self.end = end
|
||||||
|
|
||||||
def __repr__(self):
|
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):
|
def iso8601(self):
|
||||||
"""Returns an ISO 8601 representation of the MayaInterval."""
|
"""Returns an ISO 8601 representation of the MayaInterval."""
|
||||||
return '{0}/{1}'.format(self.start.iso8601(), self.end.iso8601())
|
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
|
@classmethod
|
||||||
def from_iso8601(cls, s):
|
def from_iso8601(cls, s):
|
||||||
# # Start and end, such as "2007-03-01T13:00:00Z/2008-05-11T15:30:00Z"
|
# # Start and end, such as "2007-03-01T13:00:00Z/2008-05-11T15:30:00Z"
|
||||||
# start, end = s.split('/')
|
start, end = s.split('/')
|
||||||
# try:
|
try:
|
||||||
# start = parse(start)
|
start = parse(start)
|
||||||
# except pendulum.parsing.exceptions.ParserError:
|
except pendulum.parsing.exceptions.ParserError:
|
||||||
# start = self._parse_iso8601_duration(start)
|
# start = self._parse_iso8601_duration(start, end=end)
|
||||||
# try:
|
raise NotImplementedError()
|
||||||
# end = parse(start)
|
|
||||||
# except pendulum.parsing.exceptions.ParserError as e:
|
try:
|
||||||
# end = self._parse_iso8601_duration(start)
|
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"
|
# # Start and duration, such as "2007-03-01T13:00:00Z/P1Y2M10DT2H30M"
|
||||||
# # Duration and end, such as "P1Y2M10DT2H30M/2008-05-11T15:30:00Z"
|
# # Duration and end, such as "P1Y2M10DT2H30M/2008-05-11T15:30:00Z"
|
||||||
raise NotImplementedError()
|
|
||||||
|
|
||||||
@validate_arguments_type_of_function()
|
@validate_arguments_type_of_function()
|
||||||
def __and__(self, maya_interval):
|
def __and__(self, maya_interval):
|
||||||
@@ -411,8 +489,7 @@ class MayaInterval(object):
|
|||||||
@validate_arguments_type_of_function()
|
@validate_arguments_type_of_function()
|
||||||
def __eq__(self, maya_interval):
|
def __eq__(self, maya_interval):
|
||||||
return (
|
return (
|
||||||
self.start == maya_interval.start and
|
self.start == maya_interval.start and self.end == maya_interval.end
|
||||||
self.end == maya_interval.end
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def __hash__(self):
|
def __hash__(self):
|
||||||
@@ -425,8 +502,8 @@ class MayaInterval(object):
|
|||||||
@validate_arguments_type_of_function()
|
@validate_arguments_type_of_function()
|
||||||
def __cmp__(self, maya_interval):
|
def __cmp__(self, maya_interval):
|
||||||
return (
|
return (
|
||||||
cmp(self.start, maya_interval.start) or
|
cmp(self.start, maya_interval.start)
|
||||||
cmp(self.end, maya_interval.end)
|
or cmp(self.end, maya_interval.end)
|
||||||
)
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -457,8 +534,9 @@ class MayaInterval(object):
|
|||||||
MayaInterval(
|
MayaInterval(
|
||||||
interval_list[0].start,
|
interval_list[0].start,
|
||||||
max(interval_list[0].end, interval_list[1].end),
|
max(interval_list[0].end, interval_list[1].end),
|
||||||
),
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
return interval_list
|
return interval_list
|
||||||
|
|
||||||
@validate_arguments_type_of_function()
|
@validate_arguments_type_of_function()
|
||||||
@@ -466,6 +544,7 @@ class MayaInterval(object):
|
|||||||
""""Removes the given interval."""
|
""""Removes the given interval."""
|
||||||
if not self & maya_interval:
|
if not self & maya_interval:
|
||||||
return [self]
|
return [self]
|
||||||
|
|
||||||
elif maya_interval.contains(self):
|
elif maya_interval.contains(self):
|
||||||
return []
|
return []
|
||||||
|
|
||||||
@@ -477,10 +556,8 @@ class MayaInterval(object):
|
|||||||
return interval_list
|
return interval_list
|
||||||
|
|
||||||
def split(self, duration, include_remainder=True):
|
def split(self, duration, include_remainder=True):
|
||||||
|
|
||||||
# Convert seconds to timedelta, if appropriate.
|
# Convert seconds to timedelta, if appropriate.
|
||||||
duration = _seconds_or_timedelta(duration)
|
duration = _seconds_or_timedelta(duration)
|
||||||
|
|
||||||
if duration <= timedelta(seconds=0):
|
if duration <= timedelta(seconds=0):
|
||||||
raise ValueError('cannot call split with a non-positive timedelta')
|
raise ValueError('cannot call split with a non-positive timedelta')
|
||||||
|
|
||||||
@@ -488,38 +565,36 @@ class MayaInterval(object):
|
|||||||
while start < self.end:
|
while start < self.end:
|
||||||
if start + duration <= self.end:
|
if start + duration <= self.end:
|
||||||
yield MayaInterval(start, start + duration)
|
yield MayaInterval(start, start + duration)
|
||||||
|
|
||||||
elif include_remainder:
|
elif include_remainder:
|
||||||
yield MayaInterval(start, self.end)
|
yield MayaInterval(start, self.end)
|
||||||
|
|
||||||
start += duration
|
start += duration
|
||||||
|
|
||||||
def quantize(self, duration, snap_out=False, timezone='UTC'):
|
def quantize(self, duration, snap_out=False, timezone='UTC'):
|
||||||
"""Returns a quanitzed interval."""
|
"""Returns a quanitzed interval."""
|
||||||
|
|
||||||
# Convert seconds to timedelta, if appropriate.
|
# Convert seconds to timedelta, if appropriate.
|
||||||
duration = _seconds_or_timedelta(duration)
|
duration = _seconds_or_timedelta(duration)
|
||||||
timezone = pytz.timezone(timezone)
|
timezone = pytz.timezone(timezone)
|
||||||
|
|
||||||
|
|
||||||
if duration <= timedelta(seconds=0):
|
if duration <= timedelta(seconds=0):
|
||||||
raise ValueError('cannot quantize by non-positive timedelta')
|
raise ValueError('cannot quantize by non-positive timedelta')
|
||||||
|
|
||||||
epoch = timezone.localize(Datetime(1970, 1, 1))
|
epoch = timezone.localize(Datetime(1970, 1, 1))
|
||||||
seconds = int(duration.total_seconds())
|
seconds = int(duration.total_seconds())
|
||||||
|
start_seconds = int(
|
||||||
start_seconds = int((self.start.datetime(naive=False) - epoch).total_seconds())
|
(self.start.datetime(naive=False) - epoch).total_seconds()
|
||||||
end_seconds = int((self.end.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:
|
if start_seconds % seconds and not snap_out:
|
||||||
start_seconds += seconds
|
start_seconds += seconds
|
||||||
if end_seconds % seconds and snap_out:
|
if end_seconds % seconds and snap_out:
|
||||||
end_seconds += seconds
|
end_seconds += seconds
|
||||||
|
|
||||||
start_seconds -= start_seconds % seconds
|
start_seconds -= start_seconds % seconds
|
||||||
end_seconds -= end_seconds % seconds
|
end_seconds -= end_seconds % seconds
|
||||||
|
|
||||||
if start_seconds > end_seconds:
|
if start_seconds > end_seconds:
|
||||||
start_seconds = end_seconds
|
start_seconds = end_seconds
|
||||||
|
|
||||||
return MayaInterval(
|
return MayaInterval(
|
||||||
start=MayaDT.from_datetime(epoch).add(seconds=start_seconds),
|
start=MayaDT.from_datetime(epoch).add(seconds=start_seconds),
|
||||||
end=MayaDT.from_datetime(epoch).add(seconds=end_seconds),
|
end=MayaDT.from_datetime(epoch).add(seconds=end_seconds),
|
||||||
@@ -528,23 +603,17 @@ class MayaInterval(object):
|
|||||||
@validate_arguments_type_of_function()
|
@validate_arguments_type_of_function()
|
||||||
def intersection(self, maya_interval):
|
def intersection(self, maya_interval):
|
||||||
"""Returns the intersection between two intervals."""
|
"""Returns the intersection between two intervals."""
|
||||||
|
|
||||||
start = max(self.start, maya_interval.start)
|
start = max(self.start, maya_interval.start)
|
||||||
end = min(self.end, maya_interval.end)
|
end = min(self.end, maya_interval.end)
|
||||||
|
|
||||||
either_instant = self.is_instant or maya_interval.is_instant
|
either_instant = self.is_instant or maya_interval.is_instant
|
||||||
instant_overlap = (
|
instant_overlap = (self.start == maya_interval.start or start <= end)
|
||||||
self.start == maya_interval.start or
|
|
||||||
start <= end
|
|
||||||
)
|
|
||||||
if (either_instant and instant_overlap) or (start < end):
|
if (either_instant and instant_overlap) or (start < end):
|
||||||
return MayaInterval(start, end)
|
return MayaInterval(start, end)
|
||||||
|
|
||||||
@validate_arguments_type_of_function()
|
@validate_arguments_type_of_function()
|
||||||
def contains(self, maya_interval):
|
def contains(self, maya_interval):
|
||||||
return (
|
return (
|
||||||
self.start <= maya_interval.start and
|
self.start <= maya_interval.start and self.end >= maya_interval.end
|
||||||
self.end >= maya_interval.end
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def __contains__(self, maya_dt):
|
def __contains__(self, maya_dt):
|
||||||
@@ -559,8 +628,7 @@ class MayaInterval(object):
|
|||||||
@validate_arguments_type_of_function()
|
@validate_arguments_type_of_function()
|
||||||
def is_adjacent(self, maya_interval):
|
def is_adjacent(self, maya_interval):
|
||||||
return (
|
return (
|
||||||
self.start == maya_interval.end or
|
self.start == maya_interval.end or self.end == maya_interval.start
|
||||||
self.end == maya_interval.start
|
|
||||||
)
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -577,14 +645,28 @@ class MayaInterval(object):
|
|||||||
""".format(
|
""".format(
|
||||||
self.start.datetime().strftime(ical_dt_format),
|
self.start.datetime().strftime(ical_dt_format),
|
||||||
self.end.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
|
@staticmethod
|
||||||
def flatten(interval_list):
|
def flatten(interval_list):
|
||||||
return functools.reduce(lambda reduced, maya_interval: (
|
return functools.reduce(
|
||||||
(reduced[:-1] + maya_interval.combine(reduced[-1]))
|
lambda reduced,
|
||||||
if reduced else [maya_interval]
|
maya_interval: (
|
||||||
), sorted(interval_list), [])
|
(
|
||||||
|
reduced[:-1] + maya_interval.combine(reduced[-1])
|
||||||
|
) if reduced else [
|
||||||
|
maya_interval
|
||||||
|
]
|
||||||
|
),
|
||||||
|
sorted(interval_list),
|
||||||
|
[],
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_datetime(cls, start_dt=None, end_dt=None, duration=None):
|
def from_datetime(cls, start_dt=None, end_dt=None, duration=None):
|
||||||
@@ -613,13 +695,14 @@ def when(string, timezone='UTC', prefer_past=False):
|
|||||||
prefer_past -- prefer parsing ambiguous date as in the past
|
prefer_past -- prefer parsing ambiguous date as in the past
|
||||||
|
|
||||||
"""
|
"""
|
||||||
settings = {'TIMEZONE': timezone, 'RETURN_AS_TIMEZONE_AWARE': True, 'TO_TIMEZONE': 'UTC'}
|
settings = {
|
||||||
|
'TIMEZONE': timezone,
|
||||||
|
'RETURN_AS_TIMEZONE_AWARE': True,
|
||||||
|
'TO_TIMEZONE': 'UTC',
|
||||||
|
}
|
||||||
if prefer_past:
|
if prefer_past:
|
||||||
settings['PREFER_DATES_FROM'] = 'past'
|
settings['PREFER_DATES_FROM'] = 'past'
|
||||||
|
|
||||||
dt = dateparser.parse(string, settings=settings)
|
dt = dateparser.parse(string, settings=settings)
|
||||||
|
|
||||||
if dt is None:
|
if dt is None:
|
||||||
raise ValueError('invalid datetime input specified.')
|
raise ValueError('invalid datetime input specified.')
|
||||||
|
|
||||||
@@ -629,21 +712,25 @@ def when(string, timezone='UTC', prefer_past=False):
|
|||||||
def parse(string, timezone='UTC', day_first=False, year_first=True):
|
def parse(string, timezone='UTC', day_first=False, year_first=True):
|
||||||
""""Returns a MayaDT instance for the machine-produced moment specified.
|
""""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:
|
Keyword Arguments:
|
||||||
string -- string to be parsed
|
string -- string to be parsed
|
||||||
timezone -- timezone referenced from (default: 'UTC')
|
timezone -- timezone referenced from (default: 'UTC')
|
||||||
day_first -- if true, the first value (e.g. 01/05/2016) is parsed as day.
|
day_first -- if true, the first value (e.g. 01/05/2016)
|
||||||
if year_first is set to True, this distinguishes between YDM and YMD. (default: False)
|
is parsed as day.
|
||||||
year_first -- if true, the first value (e.g. 2016/05/01) is parsed as year (default: True)
|
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 = {}
|
||||||
options['tz'] = timezone
|
options['tz'] = timezone
|
||||||
options['day_first'] = day_first
|
options['day_first'] = day_first
|
||||||
options['year_first'] = year_first
|
options['year_first'] = year_first
|
||||||
|
|
||||||
dt = pendulum.parse(string, **options)
|
dt = pendulum.parse(str(string), **options)
|
||||||
return MayaDT.from_datetime(dt)
|
return MayaDT.from_datetime(dt)
|
||||||
|
|
||||||
|
|
||||||
@@ -658,17 +745,22 @@ def _seconds_or_timedelta(duration):
|
|||||||
elif isinstance(duration, timedelta):
|
elif isinstance(duration, timedelta):
|
||||||
dt_timedelta = duration
|
dt_timedelta = duration
|
||||||
else:
|
else:
|
||||||
raise TypeError('Expects argument as `datetime.timedelta` object '
|
raise TypeError(
|
||||||
'or seconds in `int` format')
|
'Expects argument as `datetime.timedelta` object '
|
||||||
|
'or seconds in `int` format'
|
||||||
|
)
|
||||||
|
|
||||||
return dt_timedelta
|
return dt_timedelta
|
||||||
|
|
||||||
|
|
||||||
def intervals(start, end, interval):
|
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)
|
interval = _seconds_or_timedelta(interval)
|
||||||
|
|
||||||
current_timestamp = start
|
current_timestamp = start
|
||||||
while current_timestamp.epoch < end.epoch:
|
while current_timestamp.epoch < end.epoch:
|
||||||
yield current_timestamp
|
yield current_timestamp
|
||||||
|
|
||||||
current_timestamp = current_timestamp.add(seconds=interval.seconds)
|
current_timestamp = current_timestamp.add(seconds=interval.seconds)
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import io
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import codecs
|
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__))
|
here = os.path.abspath(os.path.dirname(__file__))
|
||||||
@@ -24,13 +26,51 @@ required = [
|
|||||||
'dateparser',
|
'dateparser',
|
||||||
'ruamel.yaml',
|
'ruamel.yaml',
|
||||||
'tzlocal',
|
'tzlocal',
|
||||||
'pendulum'
|
'pendulum',
|
||||||
|
'snaptime'
|
||||||
]
|
]
|
||||||
|
|
||||||
packages = [
|
packages = [
|
||||||
'maya',
|
'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 to store version and package info
|
||||||
about = dict()
|
about = dict()
|
||||||
with codecs.open(os.path.join(here, 'maya', '__version__.py'), 'r', encoding='utf-8') as f:
|
with codecs.open(os.path.join(here, 'maya', '__version__.py'), 'r', encoding='utf-8') as f:
|
||||||
@@ -61,4 +101,7 @@ setup(
|
|||||||
'Programming Language :: Python :: Implementation :: CPython',
|
'Programming Language :: Python :: Implementation :: CPython',
|
||||||
'Topic :: Software Development :: Libraries :: Python Modules'
|
'Topic :: Software Development :: Libraries :: Python Modules'
|
||||||
),
|
),
|
||||||
|
cmdclass={
|
||||||
|
'upload': UploadCommand,
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|||||||
+28
-48
@@ -1,8 +1,9 @@
|
|||||||
import pytest
|
|
||||||
import pytz
|
|
||||||
import copy
|
import copy
|
||||||
import time
|
import time
|
||||||
from datetime import date, timedelta, datetime as Datetime
|
from datetime import timedelta, datetime as Datetime
|
||||||
|
|
||||||
|
import pytz
|
||||||
|
import pytest
|
||||||
|
|
||||||
import maya
|
import maya
|
||||||
from maya.core import _seconds_or_timedelta # import private function
|
from maya.core import _seconds_or_timedelta # import private function
|
||||||
@@ -26,43 +27,30 @@ def test_parse_iso8601():
|
|||||||
string = '20161001T1430.4+05:30'
|
string = '20161001T1430.4+05:30'
|
||||||
expected = '2016-10-01T09:00:00.400000Z'
|
expected = '2016-10-01T09:00:00.400000Z'
|
||||||
d = maya.MayaDT.from_iso8601(string)
|
d = maya.MayaDT.from_iso8601(string)
|
||||||
|
|
||||||
assert expected == d.iso8601()
|
assert expected == d.iso8601()
|
||||||
|
|
||||||
string = '2016T14'
|
string = '2016T14'
|
||||||
expected = '2016-01-01T14:00:00Z'
|
expected = '2016-01-01T14:00:00Z'
|
||||||
d = maya.MayaDT.from_iso8601(string)
|
d = maya.MayaDT.from_iso8601(string)
|
||||||
|
|
||||||
assert expected == d.iso8601()
|
assert expected == d.iso8601()
|
||||||
|
|
||||||
string = '2016-10T14'
|
string = '2016-10T14'
|
||||||
expected = '2016-10-01T14:00:00Z'
|
expected = '2016-10-01T14:00:00Z'
|
||||||
d = maya.MayaDT.from_iso8601(string)
|
d = maya.MayaDT.from_iso8601(string)
|
||||||
|
|
||||||
assert expected == d.iso8601()
|
assert expected == d.iso8601()
|
||||||
|
|
||||||
string = '2012W05'
|
string = '2012W05'
|
||||||
expected = '2012-01-30T00:00:00Z'
|
expected = '2012-01-30T00:00:00Z'
|
||||||
d = maya.MayaDT.from_iso8601(string)
|
d = maya.MayaDT.from_iso8601(string)
|
||||||
|
|
||||||
assert expected == d.iso8601()
|
assert expected == d.iso8601()
|
||||||
|
|
||||||
string = '2012W055'
|
string = '2012W055'
|
||||||
expected = '2012-02-03T00:00:00Z'
|
expected = '2012-02-03T00:00:00Z'
|
||||||
d = maya.MayaDT.from_iso8601(string)
|
d = maya.MayaDT.from_iso8601(string)
|
||||||
|
|
||||||
assert expected == d.iso8601()
|
assert expected == d.iso8601()
|
||||||
|
|
||||||
string = '2012007'
|
string = '2012007'
|
||||||
expected = '2012-01-07T00:00:00Z'
|
expected = '2012-01-07T00:00:00Z'
|
||||||
d = maya.MayaDT.from_iso8601(string)
|
d = maya.MayaDT.from_iso8601(string)
|
||||||
|
|
||||||
assert expected == d.iso8601()
|
assert expected == d.iso8601()
|
||||||
|
|
||||||
string = '2016-W07T09'
|
string = '2016-W07T09'
|
||||||
expected = '2016-02-15T09:00:00Z'
|
expected = '2016-02-15T09:00:00Z'
|
||||||
d = maya.MayaDT.from_iso8601(string)
|
d = maya.MayaDT.from_iso8601(string)
|
||||||
|
|
||||||
assert expected == d.iso8601()
|
assert expected == d.iso8601()
|
||||||
|
|
||||||
|
|
||||||
@@ -71,13 +59,14 @@ def test_struct():
|
|||||||
ts = time.gmtime(now)
|
ts = time.gmtime(now)
|
||||||
m = maya.MayaDT.from_struct(ts)
|
m = maya.MayaDT.from_struct(ts)
|
||||||
dt = Datetime.fromtimestamp(now, pytz.UTC)
|
dt = Datetime.fromtimestamp(now, pytz.UTC)
|
||||||
assert m._epoch != None
|
assert m._epoch is not None
|
||||||
assert m.datetime() == dt
|
assert m.datetime() == dt
|
||||||
|
|
||||||
ts = time.localtime(now)
|
ts = time.localtime(now)
|
||||||
m = maya.MayaDT.from_struct(ts)
|
m = maya.MayaDT.from_struct(ts)
|
||||||
dt = Datetime.fromtimestamp(time.mktime(ts) - maya.core.utc_offset(), pytz.UTC)
|
dt = Datetime.fromtimestamp(
|
||||||
assert m._epoch != None
|
time.mktime(ts) - maya.core.utc_offset(), pytz.UTC
|
||||||
|
)
|
||||||
|
assert m._epoch is not None
|
||||||
assert m.datetime() == dt
|
assert m.datetime() == dt
|
||||||
|
|
||||||
|
|
||||||
@@ -94,14 +83,12 @@ def test_issue_104():
|
|||||||
def test_human_when():
|
def test_human_when():
|
||||||
r1 = maya.when('yesterday')
|
r1 = maya.when('yesterday')
|
||||||
r2 = maya.when('today')
|
r2 = maya.when('today')
|
||||||
|
|
||||||
assert (r2.day - r1.day) in (1, -30, -29, -28, -27)
|
assert (r2.day - r1.day) in (1, -30, -29, -28, -27)
|
||||||
|
|
||||||
|
|
||||||
def test_machine_parse():
|
def test_machine_parse():
|
||||||
r1 = maya.parse('August 14, 2015')
|
r1 = maya.parse('August 14, 2015')
|
||||||
assert r1.day == 14
|
assert r1.day == 14
|
||||||
|
|
||||||
r2 = maya.parse('August 15, 2015')
|
r2 = maya.parse('August 15, 2015')
|
||||||
assert r2.day == 15
|
assert r2.day == 15
|
||||||
|
|
||||||
@@ -115,14 +102,12 @@ def test_dt_tz_translation():
|
|||||||
def test_dt_tz_naive():
|
def test_dt_tz_naive():
|
||||||
d1 = maya.now().datetime(naive=True)
|
d1 = maya.now().datetime(naive=True)
|
||||||
assert d1.tzinfo is None
|
assert d1.tzinfo is None
|
||||||
|
|
||||||
d2 = maya.now().datetime(to_timezone='EST', naive=True)
|
d2 = maya.now().datetime(to_timezone='EST', naive=True)
|
||||||
assert d2.tzinfo is None
|
assert d2.tzinfo is None
|
||||||
assert (d1.hour - d2.hour) % 24 == 5
|
assert (d1.hour - d2.hour) % 24 == 5
|
||||||
|
|
||||||
|
|
||||||
def test_random_date():
|
def test_random_date():
|
||||||
|
|
||||||
# Test properties for maya.when()
|
# Test properties for maya.when()
|
||||||
d1 = maya.when('11-17-11 08:09:10')
|
d1 = maya.when('11-17-11 08:09:10')
|
||||||
assert d1.year == 2011
|
assert d1.year == 2011
|
||||||
@@ -134,7 +119,6 @@ def test_random_date():
|
|||||||
assert d1.minute == 9
|
assert d1.minute == 9
|
||||||
assert d1.second == 10
|
assert d1.second == 10
|
||||||
assert d1.microsecond == 0
|
assert d1.microsecond == 0
|
||||||
|
|
||||||
# Test properties for maya.parse()
|
# Test properties for maya.parse()
|
||||||
d2 = maya.parse('February 29, 1992 13:12:34')
|
d2 = maya.parse('February 29, 1992 13:12:34')
|
||||||
assert d2.year == 1992
|
assert d2.year == 1992
|
||||||
@@ -150,10 +134,8 @@ def test_random_date():
|
|||||||
|
|
||||||
def test_print_date(capsys):
|
def test_print_date(capsys):
|
||||||
d = maya.when('11-17-11')
|
d = maya.when('11-17-11')
|
||||||
|
|
||||||
print(d)
|
print(d)
|
||||||
out, err = capsys.readouterr()
|
out, err = capsys.readouterr()
|
||||||
|
|
||||||
assert out == 'Thu, 17 Nov 2011 00:00:00 GMT\n'
|
assert out == 'Thu, 17 Nov 2011 00:00:00 GMT\n'
|
||||||
assert repr(d) == '<MayaDT epoch=1321488000.0>'
|
assert repr(d) == '<MayaDT epoch=1321488000.0>'
|
||||||
|
|
||||||
@@ -169,29 +151,23 @@ def test_slang_date():
|
|||||||
|
|
||||||
|
|
||||||
def test_slang_time():
|
def test_slang_time():
|
||||||
d = maya.when('one hour ago')
|
d = maya.when('1 hour ago')
|
||||||
assert d.slang_time() == 'an hour ago'
|
assert d.slang_time() == 'an hour ago'
|
||||||
|
|
||||||
|
|
||||||
def test_parse():
|
def test_parse():
|
||||||
d = maya.parse('February 21, 1994')
|
d = maya.parse('February 21, 1994')
|
||||||
assert format(d) == '1994-02-21 00:00:00+00:00'
|
assert format(d) == '1994-02-21 00:00:00+00:00'
|
||||||
|
|
||||||
d = maya.parse('01/05/2016')
|
d = maya.parse('01/05/2016')
|
||||||
assert format(d) == '2016-01-05 00:00:00+00:00'
|
assert format(d) == '2016-01-05 00:00:00+00:00'
|
||||||
|
|
||||||
d = maya.parse('01/05/2016', day_first=True)
|
d = maya.parse('01/05/2016', day_first=True)
|
||||||
assert format(d) == '2016-05-01 00:00:00+00:00'
|
assert format(d) == '2016-05-01 00:00:00+00:00'
|
||||||
|
|
||||||
d = maya.parse('2016/05/01', year_first=True, day_first=False)
|
d = maya.parse('2016/05/01', year_first=True, day_first=False)
|
||||||
assert format(d) == '2016-05-01 00:00:00+00:00'
|
assert format(d) == '2016-05-01 00:00:00+00:00'
|
||||||
|
|
||||||
d = maya.parse('2016/01/05', year_first=True, day_first=True)
|
d = maya.parse('2016/01/05', year_first=True, day_first=True)
|
||||||
assert format(d) == '2016-05-01 00:00:00+00:00'
|
assert format(d) == '2016-05-01 00:00:00+00:00'
|
||||||
|
|
||||||
d = maya.parse('01/05/2016', timezone='UTC')
|
d = maya.parse('01/05/2016', timezone='UTC')
|
||||||
assert format(d) == '2016-01-05 00:00:00+00:00'
|
assert format(d) == '2016-01-05 00:00:00+00:00'
|
||||||
|
|
||||||
d = maya.parse('01/05/2016', timezone='US/Central')
|
d = maya.parse('01/05/2016', timezone='US/Central')
|
||||||
assert format(d) == '2016-01-05 06:00:00+00:00'
|
assert format(d) == '2016-01-05 06:00:00+00:00'
|
||||||
|
|
||||||
@@ -200,10 +176,8 @@ def test_when_past():
|
|||||||
next_month = str(maya.now().add(months=1).month)
|
next_month = str(maya.now().add(months=1).month)
|
||||||
this_year = maya.now().year
|
this_year = maya.now().year
|
||||||
last_year = this_year - 1
|
last_year = this_year - 1
|
||||||
|
|
||||||
future_date = maya.when(next_month)
|
future_date = maya.when(next_month)
|
||||||
past_date = maya.when(next_month, prefer_past=True)
|
past_date = maya.when(next_month, prefer_past=True)
|
||||||
|
|
||||||
assert future_date.year == this_year
|
assert future_date.year == this_year
|
||||||
if next_month == '1':
|
if next_month == '1':
|
||||||
assert past_date.year == this_year
|
assert past_date.year == this_year
|
||||||
@@ -217,7 +191,7 @@ def test_datetime_to_timezone():
|
|||||||
|
|
||||||
|
|
||||||
def test_rfc3339():
|
def test_rfc3339():
|
||||||
mdt = maya.when('2016-01-01')
|
mdt = maya.when('2016-01-01')
|
||||||
out = mdt.rfc3339()
|
out = mdt.rfc3339()
|
||||||
mdt2 = maya.MayaDT.from_rfc3339(out)
|
mdt2 = maya.MayaDT.from_rfc3339(out)
|
||||||
assert mdt.epoch == mdt2.epoch
|
assert mdt.epoch == mdt2.epoch
|
||||||
@@ -227,25 +201,18 @@ def test_comparison_operations():
|
|||||||
now = maya.now()
|
now = maya.now()
|
||||||
now_copy = copy.deepcopy(now)
|
now_copy = copy.deepcopy(now)
|
||||||
tomorrow = maya.when('tomorrow')
|
tomorrow = maya.when('tomorrow')
|
||||||
|
|
||||||
assert (now == now_copy) is True
|
assert (now == now_copy) is True
|
||||||
assert (now == tomorrow) is False
|
assert (now == tomorrow) is False
|
||||||
|
|
||||||
assert (now != now_copy) is False
|
assert (now != now_copy) is False
|
||||||
assert (now != tomorrow) is True
|
assert (now != tomorrow) is True
|
||||||
|
|
||||||
assert (now < now_copy) is False
|
assert (now < now_copy) is False
|
||||||
assert (now < tomorrow) is True
|
assert (now < tomorrow) is True
|
||||||
|
|
||||||
assert (now <= now_copy) is True
|
assert (now <= now_copy) is True
|
||||||
assert (now <= tomorrow) is True
|
assert (now <= tomorrow) is True
|
||||||
|
|
||||||
assert (now > now_copy) is False
|
assert (now > now_copy) is False
|
||||||
assert (now > tomorrow) is False
|
assert (now > tomorrow) is False
|
||||||
|
|
||||||
assert (now >= now_copy) is True
|
assert (now >= now_copy) is True
|
||||||
assert (now >= tomorrow) is False
|
assert (now >= tomorrow) is False
|
||||||
|
|
||||||
# Check Exceptions
|
# Check Exceptions
|
||||||
with pytest.raises(TypeError):
|
with pytest.raises(TypeError):
|
||||||
now == 1
|
now == 1
|
||||||
@@ -274,8 +241,7 @@ def test_seconds_or_timedelta():
|
|||||||
def test_intervals():
|
def test_intervals():
|
||||||
now = maya.now()
|
now = maya.now()
|
||||||
tomorrow = now.add(days=1)
|
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
|
|
||||||
|
|
||||||
|
|
||||||
def test_dunder_add():
|
def test_dunder_add():
|
||||||
@@ -299,18 +265,32 @@ def test_dunder_sub():
|
|||||||
def test_mayaDT_sub():
|
def test_mayaDT_sub():
|
||||||
now = maya.now()
|
now = maya.now()
|
||||||
then = now.add(days=1)
|
then = now.add(days=1)
|
||||||
assert then - now == timedelta(24*60*60)
|
assert then - now == timedelta(24 * 60 * 60)
|
||||||
assert now - then == timedelta(-24*60*60)
|
assert now - then == timedelta(-24 * 60 * 60)
|
||||||
|
|
||||||
|
|
||||||
def test_core_local_timezone(monkeypatch):
|
def test_core_local_timezone(monkeypatch):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def mock_local_tz(self):
|
def mock_local_tz(self):
|
||||||
|
|
||||||
class StaticTzInfo(object):
|
class StaticTzInfo(object):
|
||||||
zone = 'local'
|
zone = 'local'
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<StaticTzInfo 'local'>"
|
return "<StaticTzInfo 'local'>"
|
||||||
|
|
||||||
return StaticTzInfo()
|
return StaticTzInfo()
|
||||||
|
|
||||||
monkeypatch.setattr(maya.MayaDT, '_local_tz', mock_local_tz)
|
monkeypatch.setattr(maya.MayaDT, '_local_tz', mock_local_tz)
|
||||||
mdt = maya.MayaDT(0)
|
mdt = maya.MayaDT(0)
|
||||||
assert mdt.local_timezone == 'UTC'
|
assert mdt.local_timezone == 'UTC'
|
||||||
|
|
||||||
|
|
||||||
|
def test_snaptime():
|
||||||
|
# given
|
||||||
|
dt = maya.when('Mon, 21 Feb 1994 21:21:42 GMT')
|
||||||
|
# when
|
||||||
|
dt = dt.snap('@d')
|
||||||
|
# then
|
||||||
|
assert dt == maya.when('Mon, 21 Feb 1994 00:00:00 GMT')
|
||||||
|
|||||||
+196
-214
@@ -15,26 +15,20 @@ Melbourne = pytz.timezone('Australia/Melbourne')
|
|||||||
def test_interval_requires_2_of_start_end_duration():
|
def test_interval_requires_2_of_start_end_duration():
|
||||||
start = maya.now()
|
start = maya.now()
|
||||||
end = start.add(hours=1)
|
end = start.add(hours=1)
|
||||||
|
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
maya.MayaInterval(start=start)
|
maya.MayaInterval(start=start)
|
||||||
|
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
maya.MayaInterval(end=end)
|
maya.MayaInterval(end=end)
|
||||||
|
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
maya.MayaInterval(duration=60)
|
maya.MayaInterval(duration=60)
|
||||||
|
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
maya.MayaInterval(start=start, end=end, duration=60)
|
maya.MayaInterval(start=start, end=end, duration=60)
|
||||||
|
|
||||||
maya.MayaInterval(start=start, end=end)
|
maya.MayaInterval(start=start, end=end)
|
||||||
maya.MayaInterval(start=start, duration=60)
|
maya.MayaInterval(start=start, duration=60)
|
||||||
maya.MayaInterval(end=end, duration=60)
|
maya.MayaInterval(end=end, duration=60)
|
||||||
|
|
||||||
|
|
||||||
def test_interval_requires_end_time_after_or_on_start_time():
|
def test_interval_requires_end_time_after_or_on_start_time():
|
||||||
|
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
maya.MayaInterval(start=maya.now(), duration=0)
|
maya.MayaInterval(start=maya.now(), duration=0)
|
||||||
maya.MayaInterval(start=maya.now(), duration=-1)
|
maya.MayaInterval(start=maya.now(), duration=-1)
|
||||||
@@ -64,7 +58,9 @@ def test_interval_init_end_duration():
|
|||||||
assert interval.start == end.subtract(seconds=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, 1, 3, (1, 2)),
|
||||||
(0, 2, 3, 4, None),
|
(0, 2, 3, 4, None),
|
||||||
(0, 2, 2, 3, None),
|
(0, 2, 2, 3, None),
|
||||||
@@ -76,7 +72,8 @@ def test_interval_init_end_duration():
|
|||||||
(1, 3, 1, 1, (1, 1)),
|
(1, 3, 1, 1, (1, 1)),
|
||||||
(2, 3, 1, 1, None),
|
(2, 3, 1, 1, None),
|
||||||
(1, 3, 2, 2, (2, 2)),
|
(1, 3, 2, 2, (2, 2)),
|
||||||
), ids=(
|
),
|
||||||
|
ids=(
|
||||||
'overlapping',
|
'overlapping',
|
||||||
'non-overlapping',
|
'non-overlapping',
|
||||||
'adjacent',
|
'adjacent',
|
||||||
@@ -87,21 +84,19 @@ def test_interval_init_end_duration():
|
|||||||
'instant overlapping',
|
'instant overlapping',
|
||||||
'instant overlapping start only (left)',
|
'instant overlapping start only (left)',
|
||||||
'instant disjoint (left)',
|
'instant disjoint (left)',
|
||||||
'instant overlapping (left)'
|
'instant overlapping (left)',
|
||||||
))
|
),
|
||||||
|
)
|
||||||
def test_interval_intersection(
|
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))
|
base = maya.MayaDT.from_datetime(datetime(2016, 1, 1))
|
||||||
interval1 = maya.MayaInterval(
|
interval1 = maya.MayaInterval(
|
||||||
base.add(days=start_doy1),
|
base.add(days=start_doy1), base.add(days=end_doy1)
|
||||||
base.add(days=end_doy1),
|
|
||||||
)
|
)
|
||||||
interval2 = maya.MayaInterval(
|
interval2 = maya.MayaInterval(
|
||||||
base.add(days=start_doy2),
|
base.add(days=start_doy2), base.add(days=end_doy2)
|
||||||
base.add(days=end_doy2),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if intersection_doys:
|
if intersection_doys:
|
||||||
start_doy_intersection, end_doy_intersection = intersection_doys
|
start_doy_intersection, end_doy_intersection = intersection_doys
|
||||||
assert interval1 & interval2 == maya.MayaInterval(
|
assert interval1 & interval2 == maya.MayaInterval(
|
||||||
@@ -110,7 +105,6 @@ def test_interval_intersection(
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
assert (interval1 & interval2) is None
|
assert (interval1 & interval2) is None
|
||||||
|
|
||||||
# check invalid argument
|
# check invalid argument
|
||||||
with pytest.raises(TypeError):
|
with pytest.raises(TypeError):
|
||||||
interval1 & 'invalid type'
|
interval1 & 'invalid type'
|
||||||
@@ -119,13 +113,10 @@ def test_interval_intersection(
|
|||||||
def test_interval_intersects():
|
def test_interval_intersects():
|
||||||
base = maya.MayaDT.from_datetime(datetime(2016, 1, 1))
|
base = maya.MayaDT.from_datetime(datetime(2016, 1, 1))
|
||||||
interval = maya.MayaInterval(base, base.add(days=1))
|
interval = maya.MayaInterval(base, base.add(days=1))
|
||||||
|
|
||||||
assert interval.intersects(interval)
|
assert interval.intersects(interval)
|
||||||
assert not interval.intersects(maya.MayaInterval(
|
assert not interval.intersects(
|
||||||
base.add(days=2),
|
maya.MayaInterval(base.add(days=2), base.add(days=3))
|
||||||
base.add(days=3),
|
)
|
||||||
))
|
|
||||||
|
|
||||||
# check invalid argument
|
# check invalid argument
|
||||||
with pytest.raises(TypeError):
|
with pytest.raises(TypeError):
|
||||||
interval.intersects('invalid type')
|
interval.intersects('invalid type')
|
||||||
@@ -135,13 +126,11 @@ def test_and_operator():
|
|||||||
base = maya.MayaDT.from_datetime(datetime(2016, 1, 1))
|
base = maya.MayaDT.from_datetime(datetime(2016, 1, 1))
|
||||||
interval1 = maya.MayaInterval(base, base.add(days=2))
|
interval1 = maya.MayaInterval(base, base.add(days=2))
|
||||||
interval2 = maya.MayaInterval(base.add(days=1), base.add(days=3))
|
interval2 = maya.MayaInterval(base.add(days=1), base.add(days=3))
|
||||||
|
|
||||||
assert (
|
assert (
|
||||||
interval1 & interval2 ==
|
interval1 & interval2 ==
|
||||||
interval2 & interval1 ==
|
interval2 & interval1 ==
|
||||||
interval1.intersection(interval2)
|
interval1.intersection(interval2)
|
||||||
)
|
)
|
||||||
|
|
||||||
# check invalid argument
|
# check invalid argument
|
||||||
with pytest.raises(TypeError):
|
with pytest.raises(TypeError):
|
||||||
interval1.intersection('invalid type')
|
interval1.intersection('invalid type')
|
||||||
@@ -151,14 +140,11 @@ def test_interval_eq_operator():
|
|||||||
start = maya.now()
|
start = maya.now()
|
||||||
end = start.add(hours=1)
|
end = start.add(hours=1)
|
||||||
interval = maya.MayaInterval(start=start, end=end)
|
interval = maya.MayaInterval(start=start, end=end)
|
||||||
|
|
||||||
assert 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))
|
assert interval != maya.MayaInterval(start=start, end=end.add(days=1))
|
||||||
|
|
||||||
# check invalid argument
|
# check invalid argument
|
||||||
with pytest.raises(TypeError):
|
with pytest.raises(TypeError):
|
||||||
interval == 'invalid type'
|
interval == 'invalid type'
|
||||||
|
|
||||||
with pytest.raises(TypeError):
|
with pytest.raises(TypeError):
|
||||||
interval != 'invalid type'
|
interval != 'invalid type'
|
||||||
|
|
||||||
@@ -167,7 +153,6 @@ def test_interval_timedelta():
|
|||||||
start = maya.now()
|
start = maya.now()
|
||||||
delta = timedelta(hours=1)
|
delta = timedelta(hours=1)
|
||||||
interval = maya.MayaInterval(start=start, duration=delta)
|
interval = maya.MayaInterval(start=start, duration=delta)
|
||||||
|
|
||||||
assert interval.timedelta == delta
|
assert interval.timedelta == delta
|
||||||
|
|
||||||
|
|
||||||
@@ -175,69 +160,55 @@ def test_interval_duration():
|
|||||||
start = maya.now()
|
start = maya.now()
|
||||||
delta = timedelta(hours=1)
|
delta = timedelta(hours=1)
|
||||||
interval = maya.MayaInterval(start=start, duration=delta)
|
interval = maya.MayaInterval(start=start, duration=delta)
|
||||||
|
|
||||||
assert interval.duration == delta.total_seconds()
|
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, 1, 3, False),
|
||||||
(0, 2, 3, 4, False),
|
(0, 2, 3, 4, False),
|
||||||
(0, 2, 2, 3, False),
|
(0, 2, 2, 3, False),
|
||||||
(0, 1, 0, 1, True),
|
(0, 1, 0, 1, True),
|
||||||
(0, 3, 1, 2, True),
|
(0, 3, 1, 2, True),
|
||||||
), ids=(
|
),
|
||||||
'overlapping',
|
ids=('overlapping', 'non-overlapping', 'adjacent', 'equal', 'subset'),
|
||||||
'non-overlapping',
|
)
|
||||||
'adjacent',
|
|
||||||
'equal',
|
|
||||||
'subset',
|
|
||||||
))
|
|
||||||
def test_interval_contains(
|
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))
|
base = maya.MayaDT.from_datetime(datetime(2016, 1, 1))
|
||||||
interval1 = maya.MayaInterval(
|
interval1 = maya.MayaInterval(
|
||||||
base.add(days=start_doy1),
|
base.add(days=start_doy1), base.add(days=end_doy1)
|
||||||
base.add(days=end_doy1),
|
|
||||||
)
|
)
|
||||||
interval2 = maya.MayaInterval(
|
interval2 = maya.MayaInterval(
|
||||||
base.add(days=start_doy2),
|
base.add(days=start_doy2), base.add(days=end_doy2)
|
||||||
base.add(days=end_doy2),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
assert interval1.contains(interval2) is expected
|
assert interval1.contains(interval2) is expected
|
||||||
assert (interval2 in interval1) is expected
|
assert (interval2 in interval1) is expected
|
||||||
|
|
||||||
# check invalid argument
|
# check invalid argument
|
||||||
with pytest.raises(TypeError):
|
with pytest.raises(TypeError):
|
||||||
interval1.contains('invalid type')
|
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, 1, False),
|
||||||
(2, 4, 2, True),
|
(2, 4, 2, True),
|
||||||
(2, 4, 3, True),
|
(2, 4, 3, True),
|
||||||
(2, 4, 4, False),
|
(2, 4, 4, False),
|
||||||
(2, 4, 5, False),
|
(2, 4, 5, False),
|
||||||
), ids=(
|
),
|
||||||
'before-start',
|
ids=('before-start', 'on-start', 'during', 'on-end', 'after-end'),
|
||||||
'on-start',
|
)
|
||||||
'during',
|
def test_interval_in_operator_maya_dt(start_doy, end_doy, dt_doy, expected):
|
||||||
'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))
|
base = maya.MayaDT.from_datetime(datetime(2016, 1, 1))
|
||||||
interval = maya.MayaInterval(
|
interval = maya.MayaInterval(
|
||||||
start=base.add(days=start_doy),
|
start=base.add(days=start_doy), end=base.add(days=end_doy)
|
||||||
end=base.add(days=end_doy),
|
|
||||||
)
|
)
|
||||||
dt = base.add(days=dt_doy)
|
dt = base.add(days=dt_doy)
|
||||||
|
|
||||||
assert (dt in interval) is expected
|
assert (dt in interval) is expected
|
||||||
|
|
||||||
# check invalid argument
|
# check invalid argument
|
||||||
with pytest.raises(TypeError):
|
with pytest.raises(TypeError):
|
||||||
'invalid type' in interval
|
'invalid type' in interval
|
||||||
@@ -247,91 +218,84 @@ def test_interval_hash():
|
|||||||
start = maya.now()
|
start = maya.now()
|
||||||
end = start.add(hours=1)
|
end = start.add(hours=1)
|
||||||
interval = maya.MayaInterval(start=start, end=end)
|
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))
|
||||||
assert hash(interval) != hash(maya.MayaInterval(
|
assert hash(interval) != hash(
|
||||||
start=start, end=end.add(days=1)))
|
maya.MayaInterval(start=start, end=end.add(days=1))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_interval_iter():
|
def test_interval_iter():
|
||||||
start = maya.now()
|
start = maya.now()
|
||||||
end = start.add(days=1)
|
end = start.add(days=1)
|
||||||
|
|
||||||
assert tuple(maya.MayaInterval(start=start, end=end)) == (start, end)
|
assert tuple(maya.MayaInterval(start=start, end=end)) == (start, end)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('start1,end1,start2,end2,expected', [
|
@pytest.mark.parametrize(
|
||||||
(1, 2, 1, 2, 0),
|
'start1,end1,start2,end2,expected',
|
||||||
(1, 3, 2, 4, -1),
|
[(1, 2, 1, 2, 0), (1, 3, 2, 4, -1), (2, 4, 1, 3, 1), (1, 2, 1, 3, -1)],
|
||||||
(2, 4, 1, 3, 1),
|
ids=(
|
||||||
(1, 2, 1, 3, -1),
|
|
||||||
], ids=(
|
|
||||||
'equal',
|
'equal',
|
||||||
'less-than',
|
'less-than',
|
||||||
'greater-than',
|
'greater-than',
|
||||||
'use-end-time-if-start-time-identical',
|
'use-end-time-if-start-time-identical',
|
||||||
))
|
),
|
||||||
|
)
|
||||||
def test_interval_cmp(start1, end1, start2, end2, expected):
|
def test_interval_cmp(start1, end1, start2, end2, expected):
|
||||||
base = maya.now()
|
base = maya.now()
|
||||||
interval1 = maya.MayaInterval(
|
interval1 = maya.MayaInterval(
|
||||||
start=base.add(days=start1),
|
start=base.add(days=start1), end=base.add(days=end1)
|
||||||
end=base.add(days=end1),
|
|
||||||
)
|
)
|
||||||
interval2 = maya.MayaInterval(
|
interval2 = maya.MayaInterval(
|
||||||
start=base.add(days=start2),
|
start=base.add(days=start2), end=base.add(days=end2)
|
||||||
end=base.add(days=end2),
|
|
||||||
)
|
)
|
||||||
assert cmp(interval1, interval2) == expected
|
assert cmp(interval1, interval2) == expected
|
||||||
|
|
||||||
# check invalid argument
|
# check invalid argument
|
||||||
with pytest.raises(TypeError):
|
with pytest.raises(TypeError):
|
||||||
cmp(interval1, 'invalid type')
|
cmp(interval1, 'invalid type')
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('start1,end1,start2,end2,expected', [
|
@pytest.mark.parametrize(
|
||||||
(1, 2, 2, 3, [(1, 3)]),
|
'start1,end1,start2,end2,expected',
|
||||||
(1, 3, 2, 4, [(1, 4)]),
|
[
|
||||||
(1, 2, 3, 4, [(1, 2), (3, 4)]),
|
(1, 2, 2, 3, [(1, 3)]),
|
||||||
(1, 5, 2, 3, [(1, 5)]),
|
(1, 3, 2, 4, [(1, 4)]),
|
||||||
], ids=(
|
(1, 2, 3, 4, [(1, 2), (3, 4)]),
|
||||||
'adjacent',
|
(1, 5, 2, 3, [(1, 5)]),
|
||||||
'overlapping',
|
],
|
||||||
'non-overlapping',
|
ids=('adjacent', 'overlapping', 'non-overlapping', 'contains'),
|
||||||
'contains',
|
)
|
||||||
))
|
|
||||||
def test_interval_combine(start1, end1, start2, end2, expected):
|
def test_interval_combine(start1, end1, start2, end2, expected):
|
||||||
base = maya.now()
|
base = maya.now()
|
||||||
interval1 = maya.MayaInterval(
|
interval1 = maya.MayaInterval(
|
||||||
start=base.add(days=start1),
|
start=base.add(days=start1), end=base.add(days=end1)
|
||||||
end=base.add(days=end1),
|
|
||||||
)
|
)
|
||||||
interval2 = maya.MayaInterval(
|
interval2 = maya.MayaInterval(
|
||||||
start=base.add(days=start2),
|
start=base.add(days=start2), end=base.add(days=end2)
|
||||||
end=base.add(days=end2),
|
|
||||||
)
|
)
|
||||||
expected_intervals = [maya.MayaInterval(
|
expected_intervals = [
|
||||||
start=base.add(days=start),
|
maya.MayaInterval(start=base.add(days=start), end=base.add(days=end))
|
||||||
end=base.add(days=end),
|
for start, end in expected
|
||||||
) for start, end in expected]
|
]
|
||||||
|
|
||||||
assert interval1.combine(interval2) == expected_intervals
|
assert interval1.combine(interval2) == expected_intervals
|
||||||
assert interval2.combine(interval1) == expected_intervals
|
assert interval2.combine(interval1) == expected_intervals
|
||||||
|
|
||||||
# check invalid argument
|
# check invalid argument
|
||||||
with pytest.raises(TypeError):
|
with pytest.raises(TypeError):
|
||||||
interval2.combine('invalid type')
|
interval2.combine('invalid type')
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('start1,end1,start2,end2,expected', [
|
@pytest.mark.parametrize(
|
||||||
(1, 2, 3, 4, [(1, 2)]),
|
'start1,end1,start2,end2,expected',
|
||||||
(1, 2, 2, 4, [(1, 2)]),
|
[
|
||||||
(2, 3, 1, 4, []),
|
(1, 2, 3, 4, [(1, 2)]),
|
||||||
(1, 4, 2, 3, [(1, 2), (3, 4)]),
|
(1, 2, 2, 4, [(1, 2)]),
|
||||||
(1, 4, 0, 2, [(2, 4)]),
|
(2, 3, 1, 4, []),
|
||||||
(1, 4, 3, 5, [(1, 3)]),
|
(1, 4, 2, 3, [(1, 2), (3, 4)]),
|
||||||
(1, 4, 1, 2, [(2, 4)]),
|
(1, 4, 0, 2, [(2, 4)]),
|
||||||
(1, 4, 3, 4, [(1, 3)]),
|
(1, 4, 3, 5, [(1, 3)]),
|
||||||
], ids=(
|
(1, 4, 1, 2, [(2, 4)]),
|
||||||
|
(1, 4, 3, 4, [(1, 3)]),
|
||||||
|
],
|
||||||
|
ids=(
|
||||||
'non-overlapping',
|
'non-overlapping',
|
||||||
'adjacent',
|
'adjacent',
|
||||||
'contains',
|
'contains',
|
||||||
@@ -340,115 +304,109 @@ def test_interval_combine(start1, end1, start2, end2, expected):
|
|||||||
'overlaps-right',
|
'overlaps-right',
|
||||||
'overlaps-left-identical-start',
|
'overlaps-left-identical-start',
|
||||||
'overlaps-right-identical-end',
|
'overlaps-right-identical-end',
|
||||||
))
|
),
|
||||||
|
)
|
||||||
def test_interval_subtract(start1, end1, start2, end2, expected):
|
def test_interval_subtract(start1, end1, start2, end2, expected):
|
||||||
base = maya.now()
|
base = maya.now()
|
||||||
interval1 = maya.MayaInterval(
|
interval1 = maya.MayaInterval(
|
||||||
start=base.add(days=start1),
|
start=base.add(days=start1), end=base.add(days=end1)
|
||||||
end=base.add(days=end1),
|
|
||||||
)
|
)
|
||||||
interval2 = maya.MayaInterval(
|
interval2 = maya.MayaInterval(
|
||||||
start=base.add(days=start2),
|
start=base.add(days=start2), end=base.add(days=end2)
|
||||||
end=base.add(days=end2),
|
|
||||||
)
|
)
|
||||||
expected_intervals = [maya.MayaInterval(
|
expected_intervals = [
|
||||||
start=base.add(days=start),
|
maya.MayaInterval(start=base.add(days=start), end=base.add(days=end))
|
||||||
end=base.add(days=end),
|
for start, end in expected
|
||||||
) for start, end in expected]
|
]
|
||||||
|
|
||||||
assert interval1.subtract(interval2) == expected_intervals
|
assert interval1.subtract(interval2) == expected_intervals
|
||||||
|
|
||||||
# check invalid argument
|
# check invalid argument
|
||||||
with pytest.raises(TypeError):
|
with pytest.raises(TypeError):
|
||||||
interval1.subtract('invalid type')
|
interval1.subtract('invalid type')
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('start1,end1,start2,end2,expected', [
|
@pytest.mark.parametrize(
|
||||||
(1, 2, 2, 3, True),
|
'start1,end1,start2,end2,expected',
|
||||||
(2, 3, 1, 2, True),
|
[
|
||||||
(1, 3, 2, 3, False),
|
(1, 2, 2, 3, True),
|
||||||
(2, 3, 4, 5, False),
|
(2, 3, 1, 2, True),
|
||||||
], ids=(
|
(1, 3, 2, 3, False),
|
||||||
'adjacent-right',
|
(2, 3, 4, 5, False),
|
||||||
'adjacent-left',
|
],
|
||||||
'overlapping',
|
ids=('adjacent-right', 'adjacent-left', 'overlapping', 'non-overlapping'),
|
||||||
'non-overlapping',
|
)
|
||||||
))
|
|
||||||
def test_interval_is_adjacent(start1, end1, start2, end2, expected):
|
def test_interval_is_adjacent(start1, end1, start2, end2, expected):
|
||||||
base = maya.now()
|
base = maya.now()
|
||||||
interval1 = maya.MayaInterval(
|
interval1 = maya.MayaInterval(
|
||||||
start=base.add(days=start1),
|
start=base.add(days=start1), end=base.add(days=end1)
|
||||||
end=base.add(days=end1),
|
|
||||||
)
|
)
|
||||||
interval2 = maya.MayaInterval(
|
interval2 = maya.MayaInterval(
|
||||||
start=base.add(days=start2),
|
start=base.add(days=start2), end=base.add(days=end2)
|
||||||
end=base.add(days=end2),
|
|
||||||
)
|
)
|
||||||
assert interval1.is_adjacent(interval2) == expected
|
assert interval1.is_adjacent(interval2) == expected
|
||||||
|
|
||||||
# check invalid argument
|
# check invalid argument
|
||||||
with pytest.raises(TypeError):
|
with pytest.raises(TypeError):
|
||||||
interval1.is_adjacent('invalid type')
|
interval1.is_adjacent('invalid type')
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('start,end,delta,include_remainder,expected', [
|
@pytest.mark.parametrize(
|
||||||
(0, 10, 5, False, [(0, 5), (5, 10)]),
|
'start,end,delta,include_remainder,expected',
|
||||||
(0, 10, 5, True, [(0, 5), (5, 10)]),
|
[
|
||||||
(0, 10, 3, False, [(0, 3), (3, 6), (6, 9)]),
|
(0, 10, 5, False, [(0, 5), (5, 10)]),
|
||||||
(0, 10, 3, True, [(0, 3), (3, 6), (6, 9), (9, 10)]),
|
(0, 10, 5, True, [(0, 5), (5, 10)]),
|
||||||
(0, 2, 5, False, []),
|
(0, 10, 3, False, [(0, 3), (3, 6), (6, 9)]),
|
||||||
(0, 2, 5, True, [(0, 2)]),
|
(0, 10, 3, True, [(0, 3), (3, 6), (6, 9), (9, 10)]),
|
||||||
], ids=(
|
(0, 2, 5, False, []),
|
||||||
|
(0, 2, 5, True, [(0, 2)]),
|
||||||
|
],
|
||||||
|
ids=(
|
||||||
'even-split',
|
'even-split',
|
||||||
'even-split-include-partial',
|
'even-split-include-partial',
|
||||||
'uneven-split-do-not-include-partial',
|
'uneven-split-do-not-include-partial',
|
||||||
'uneven-split-include-partial',
|
'uneven-split-include-partial',
|
||||||
'delta-larger-than-timepsan-do-not-include-partial',
|
'delta-larger-than-timepsan-do-not-include-partial',
|
||||||
'delta-larger-than-timepsan-include-partial',
|
'delta-larger-than-timepsan-include-partial',
|
||||||
))
|
),
|
||||||
|
)
|
||||||
def test_interval_split(start, end, delta, include_remainder, expected):
|
def test_interval_split(start, end, delta, include_remainder, expected):
|
||||||
base = maya.now()
|
base = maya.now()
|
||||||
interval = maya.MayaInterval(
|
interval = maya.MayaInterval(
|
||||||
start=base.add(days=start),
|
start=base.add(days=start), end=base.add(days=end)
|
||||||
end=base.add(days=end),
|
|
||||||
)
|
)
|
||||||
delta = timedelta(days=delta)
|
delta = timedelta(days=delta)
|
||||||
|
|
||||||
expected_intervals = [
|
expected_intervals = [
|
||||||
maya.MayaInterval(
|
maya.MayaInterval(start=base.add(days=s), end=base.add(days=e))
|
||||||
start=base.add(days=s),
|
for s, e in expected
|
||||||
end=base.add(days=e),
|
|
||||||
) for s, e in expected
|
|
||||||
]
|
]
|
||||||
|
assert expected_intervals == list(
|
||||||
assert expected_intervals == list(interval.split(
|
interval.split(delta, include_remainder=include_remainder)
|
||||||
delta, include_remainder=include_remainder))
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_interval_split_non_positive_delta():
|
def test_interval_split_non_positive_delta():
|
||||||
start = maya.now()
|
start = maya.now()
|
||||||
end = start.add(days=1)
|
end = start.add(days=1)
|
||||||
interval = maya.MayaInterval(start=start, end=end)
|
interval = maya.MayaInterval(start=start, end=end)
|
||||||
|
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
list(interval.split(timedelta(seconds=0)))
|
list(interval.split(timedelta(seconds=0)))
|
||||||
|
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
list(interval.split(timedelta(seconds=-10)))
|
list(interval.split(timedelta(seconds=-10)))
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('start,end,minutes,timezone,snap_out,expected_start,expected_end', [
|
@pytest.mark.parametrize(
|
||||||
((5, 12), (8, 48), 30, None, False, (5, 30), (8, 30)),
|
'start,end,minutes,timezone,snap_out,expected_start,expected_end',
|
||||||
((5, 12), (8, 48), 30, None, True, (5, 0), (9, 0)),
|
[
|
||||||
((5, 15), (9, 0), 15, None, False, (5, 15), (9, 0)),
|
((5, 12), (8, 48), 30, None, False, (5, 30), (8, 30)),
|
||||||
((5, 15), (9, 0), 15, None, True, (5, 15), (9, 0)),
|
((5, 12), (8, 48), 30, None, True, (5, 0), (9, 0)),
|
||||||
((6, 50), (9, 15), 60, 'America/New_York', False, (7, 0), (9, 0)),
|
((5, 15), (9, 0), 15, None, False, (5, 15), (9, 0)),
|
||||||
((6, 50), (9, 15), 60, 'America/New_York', True, (6, 0), (10, 0)),
|
((5, 15), (9, 0), 15, None, True, (5, 15), (9, 0)),
|
||||||
((6, 20), (6, 50), 60, None, False, (6, 0), (6, 0)),
|
((6, 50), (9, 15), 60, 'America/New_York', False, (7, 0), (9, 0)),
|
||||||
((6, 20), (6, 50), 60, None, True, (6, 0), (7, 0)),
|
((6, 50), (9, 15), 60, 'America/New_York', True, (6, 0), (10, 0)),
|
||||||
((6, 20), (6, 50), 60, 'America/Chicago', False, (6, 0), (6, 0)),
|
((6, 20), (6, 50), 60, None, False, (6, 0), (6, 0)),
|
||||||
((6, 20), (6, 50), 60, 'America/Chicago', True, (6, 0), (7, 0)),
|
((6, 20), (6, 50), 60, None, True, (6, 0), (7, 0)),
|
||||||
], ids=(
|
((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',
|
||||||
'normal-snap_out',
|
'normal-snap_out',
|
||||||
'already-quantized',
|
'already-quantized',
|
||||||
@@ -459,21 +417,20 @@ def test_interval_split_non_positive_delta():
|
|||||||
'too-small-snap_out',
|
'too-small-snap_out',
|
||||||
'too-small-with-timezone',
|
'too-small-with-timezone',
|
||||||
'too-small-with-timezone-snap_out',
|
'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))
|
base = maya.MayaDT.from_datetime(datetime(2017, 1, 1))
|
||||||
interval = maya.MayaInterval(
|
interval = maya.MayaInterval(
|
||||||
start=base.add(hours=start[0], minutes=start[1]),
|
start=base.add(hours=start[0], minutes=start[1]),
|
||||||
end=base.add(hours=end[0], minutes=end[1]),
|
end=base.add(hours=end[0], minutes=end[1]),
|
||||||
)
|
)
|
||||||
|
|
||||||
kwargs = {'timezone': timezone} if timezone is not None else {}
|
kwargs = {'timezone': timezone} if timezone is not None else {}
|
||||||
quantized_interval = interval.quantize(
|
quantized_interval = interval.quantize(
|
||||||
timedelta(minutes=minutes),
|
timedelta(minutes=minutes), snap_out=snap_out, **kwargs
|
||||||
snap_out=snap_out,
|
|
||||||
**kwargs
|
|
||||||
)
|
)
|
||||||
|
|
||||||
assert quantized_interval == maya.MayaInterval(
|
assert quantized_interval == maya.MayaInterval(
|
||||||
start=base.add(hours=expected_start[0], minutes=expected_start[1]),
|
start=base.add(hours=expected_start[0], minutes=expected_start[1]),
|
||||||
end=base.add(hours=expected_end[0], minutes=expected_end[1]),
|
end=base.add(hours=expected_end[0], minutes=expected_end[1]),
|
||||||
@@ -484,7 +441,6 @@ def test_quantize_invalid_delta():
|
|||||||
start = maya.now()
|
start = maya.now()
|
||||||
end = start.add(days=1)
|
end = start.add(days=1)
|
||||||
interval = maya.MayaInterval(start=start, end=end)
|
interval = maya.MayaInterval(start=start, end=end)
|
||||||
|
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
interval.quantize(timedelta(minutes=0))
|
interval.quantize(timedelta(minutes=0))
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
@@ -495,12 +451,13 @@ def test_interval_flatten_non_overlapping():
|
|||||||
step = 2
|
step = 2
|
||||||
max_hour = 20
|
max_hour = 20
|
||||||
base = maya.now()
|
base = maya.now()
|
||||||
intervals = [maya.MayaInterval(
|
intervals = [
|
||||||
start=base.add(hours=hour),
|
maya.MayaInterval(
|
||||||
duration=timedelta(hours=step - 1),
|
start=base.add(hours=hour), duration=timedelta(hours=step - 1)
|
||||||
) for hour in range(0, max_hour, step)]
|
)
|
||||||
|
for hour in range(0, max_hour, step)
|
||||||
|
]
|
||||||
random.shuffle(intervals)
|
random.shuffle(intervals)
|
||||||
|
|
||||||
assert maya.MayaInterval.flatten(intervals) == sorted(intervals)
|
assert maya.MayaInterval.flatten(intervals) == sorted(intervals)
|
||||||
|
|
||||||
|
|
||||||
@@ -508,35 +465,35 @@ def test_interval_flatten_adjacent():
|
|||||||
step = 2
|
step = 2
|
||||||
max_hour = 20
|
max_hour = 20
|
||||||
base = maya.when('jan/1/2011')
|
base = maya.when('jan/1/2011')
|
||||||
|
|
||||||
intervals = [
|
intervals = [
|
||||||
maya.MayaInterval(
|
maya.MayaInterval(
|
||||||
start=base.add(hours=hour),
|
start=base.add(hours=hour), duration=timedelta(hours=step)
|
||||||
duration=timedelta(hours=step),
|
)
|
||||||
) for hour in range(0, max_hour, step)
|
for hour in range(0, max_hour, step)
|
||||||
]
|
]
|
||||||
random.shuffle(intervals)
|
random.shuffle(intervals)
|
||||||
|
assert maya.MayaInterval.flatten(intervals) == [
|
||||||
assert maya.MayaInterval.flatten(intervals) == [maya.MayaInterval(
|
maya.MayaInterval(start=base, duration=timedelta(hours=max_hour))
|
||||||
start=base,
|
]
|
||||||
duration=timedelta(hours=max_hour),
|
|
||||||
)]
|
|
||||||
|
|
||||||
|
|
||||||
def test_interval_flatten_intersecting():
|
def test_interval_flatten_intersecting():
|
||||||
step = 2
|
step = 2
|
||||||
max_hour = 20
|
max_hour = 20
|
||||||
base = maya.now()
|
base = maya.now()
|
||||||
intervals = [maya.MayaInterval(
|
intervals = [
|
||||||
start=base.add(hours=hour),
|
maya.MayaInterval(
|
||||||
duration=timedelta(hours=step, minutes=30),
|
start=base.add(hours=hour),
|
||||||
) for hour in range(0, max_hour, step)]
|
duration=timedelta(hours=step, minutes=30),
|
||||||
|
)
|
||||||
|
for hour in range(0, max_hour, step)
|
||||||
|
]
|
||||||
random.shuffle(intervals)
|
random.shuffle(intervals)
|
||||||
|
assert maya.MayaInterval.flatten(intervals) == [
|
||||||
assert maya.MayaInterval.flatten(intervals) == [maya.MayaInterval(
|
maya.MayaInterval(
|
||||||
start=base,
|
start=base, duration=timedelta(hours=max_hour, minutes=30)
|
||||||
duration=timedelta(hours=max_hour, minutes=30),
|
)
|
||||||
)]
|
]
|
||||||
|
|
||||||
|
|
||||||
def test_interval_flatten_containing():
|
def test_interval_flatten_containing():
|
||||||
@@ -544,16 +501,16 @@ def test_interval_flatten_containing():
|
|||||||
max_hour = 20
|
max_hour = 20
|
||||||
base = maya.now()
|
base = maya.now()
|
||||||
containing_interval = maya.MayaInterval(
|
containing_interval = maya.MayaInterval(
|
||||||
start=base,
|
start=base, end=base.add(hours=max_hour + step)
|
||||||
end=base.add(hours=max_hour + step),
|
|
||||||
)
|
)
|
||||||
intervals = [maya.MayaInterval(
|
intervals = [
|
||||||
start=base.add(hours=hour),
|
maya.MayaInterval(
|
||||||
duration=timedelta(hours=step - 1),
|
start=base.add(hours=hour), duration=timedelta(hours=step - 1)
|
||||||
) for hour in range(2, max_hour, step)]
|
)
|
||||||
|
for hour in range(2, max_hour, step)
|
||||||
|
]
|
||||||
intervals.append(containing_interval)
|
intervals.append(containing_interval)
|
||||||
random.shuffle(intervals)
|
random.shuffle(intervals)
|
||||||
|
|
||||||
assert maya.MayaInterval.flatten(intervals) == [containing_interval]
|
assert maya.MayaInterval.flatten(intervals) == [containing_interval]
|
||||||
|
|
||||||
|
|
||||||
@@ -561,24 +518,18 @@ def test_interval_from_datetime():
|
|||||||
start = maya.now()
|
start = maya.now()
|
||||||
duration = timedelta(hours=1)
|
duration = timedelta(hours=1)
|
||||||
end = start + duration
|
end = start + duration
|
||||||
|
|
||||||
interval = maya.MayaInterval.from_datetime(
|
interval = maya.MayaInterval.from_datetime(
|
||||||
start_dt=start.datetime(naive=False),
|
start_dt=start.datetime(naive=False), end_dt=end.datetime(naive=False)
|
||||||
end_dt=end.datetime(naive=False),
|
|
||||||
)
|
)
|
||||||
assert interval.start == start
|
assert interval.start == start
|
||||||
assert interval.end == end
|
assert interval.end == end
|
||||||
|
|
||||||
interval2 = maya.MayaInterval.from_datetime(
|
interval2 = maya.MayaInterval.from_datetime(
|
||||||
start_dt=start.datetime(naive=False),
|
start_dt=start.datetime(naive=False), duration=duration
|
||||||
duration=duration,
|
|
||||||
)
|
)
|
||||||
assert interval2.start == start
|
assert interval2.start == start
|
||||||
assert interval2.end == end
|
assert interval2.end == end
|
||||||
|
|
||||||
interval3 = maya.MayaInterval.from_datetime(
|
interval3 = maya.MayaInterval.from_datetime(
|
||||||
end_dt=end.datetime(naive=False),
|
end_dt=end.datetime(naive=False), duration=duration
|
||||||
duration=duration,
|
|
||||||
)
|
)
|
||||||
assert interval3.start == start
|
assert interval3.start == start
|
||||||
assert interval3.end == end
|
assert interval3.end == end
|
||||||
@@ -588,3 +539,34 @@ def test_interval_iso8601():
|
|||||||
start = maya.when('11-17-11 08:09:10')
|
start = maya.when('11-17-11 08:09:10')
|
||||||
interval = maya.MayaInterval(start=start, duration=1)
|
interval = maya.MayaInterval(start=start, duration=1)
|
||||||
assert interval.iso8601() == '2011-11-17T08:09:10Z/2011-11-17T08:09:11Z'
|
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
|
||||||
|
|||||||
Reference in New Issue
Block a user