From aa87723a0c2d5d9abefc5b12f44d8f3c59a8c233 Mon Sep 17 00:00:00 2001 From: Timo Furrer Date: Wed, 14 Mar 2018 20:17:35 +0100 Subject: [PATCH 1/4] Make tests and docs a phony makefile target --- Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Makefile b/Makefile index 32167b8..fbc6789 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,5 @@ +.PHONY: tests docs + tests: pytest tests/ docs: From 0c206ad0db9f6b03fc67d192398ff72a17667e3c Mon Sep 17 00:00:00 2001 From: Timo Furrer Date: Sat, 17 Mar 2018 11:52:47 +0100 Subject: [PATCH 2/4] Fix tests --- tests/test_maya.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_maya.py b/tests/test_maya.py index 3300f5b..6eea720 100644 --- a/tests/test_maya.py +++ b/tests/test_maya.py @@ -169,7 +169,7 @@ def test_slang_date(): def test_slang_time(): - d = maya.when('one hour ago') + d = maya.when('1 hour ago') assert d.slang_time() == 'an hour ago' From 13e9f2d89698c78ef02752c795cf38525128dacd Mon Sep 17 00:00:00 2001 From: Timo Furrer Date: Wed, 14 Mar 2018 20:11:12 +0100 Subject: [PATCH 3/4] Implement snap modifiers with maya.snap(). Closes #94 --- Pipfile | 6 +- Pipfile.lock | 236 +++++++++++++++++++++++++-------------------- README.rst | 21 ++-- maya/core.py | 12 ++- setup.py | 3 +- tests/test_maya.py | 11 +++ 6 files changed, 171 insertions(+), 118 deletions(-) diff --git a/Pipfile b/Pipfile index ff3033a..32f8d2c 100644 --- a/Pipfile +++ b/Pipfile @@ -1,11 +1,15 @@ [dev-packages] + pytest = "*" -Sphinx = "*" +sphinx = "*" + [packages] + humanize = "*" pytz = "*" dateparser = "*" "ruamel.yaml" = "*" tzlocal = "*" pendulum = ">=1.0" +snaptime = "*" diff --git a/Pipfile.lock b/Pipfile.lock index 3867007..abf1bdb 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,20 +1,7 @@ { "_meta": { "hash": { - "sha256": "e7c76d32bf7d24c6e3637eb39049c1543ba59729fe50da193414bde84a120587" - }, - "host-environment-markers": { - "implementation_name": "cpython", - "implementation_version": "3.6.1", - "os_name": "posix", - "platform_machine": "x86_64", - "platform_python_implementation": "CPython", - "platform_release": "17.3.0", - "platform_system": "Darwin", - "platform_version": "Darwin Kernel Version 17.3.0: Thu Nov 9 18:09:22 PST 2017; root:xnu-4570.31.3~1/RELEASE_X86_64", - "python_full_version": "3.6.1", - "python_version": "3.6", - "sys_platform": "darwin" + "sha256": "3abd33d2f9beea083faca673fa3864252623b284fa24f9a8b939aed12428318c" }, "pipfile-spec": 6, "requires": {}, @@ -29,111 +16,124 @@ "default": { "dateparser": { "hashes": [ - "sha256:e2629d2f8361722c6047138ca085256c9f2cf5cc657fd66122aa0564afa4dc33", - "sha256:f8c24317120b06f71691d28076764ec084a132be2a250a78fdf54f6b427cac95" + "sha256:940828183c937bcec530753211b70f673c0a9aab831e43273489b310538dff86", + "sha256:b452ef8b36cd78ae86a50721794bc674aa3994e19b570f7ba92810f4e0a2ae03" ], - "version": "==0.6.0" + "index": "pypi", + "version": "==0.7.0" }, "humanize": { "hashes": [ "sha256:a43f57115831ac7c70de098e6ac46ac13be00d69abbf60bdcac251344785bb19" ], + "index": "pypi", "version": "==0.5.1" }, "pendulum": { "hashes": [ - "sha256:9196f0aa4eec534aaf02b45c47dccc6f74a255ecdab6c772cb6bcea6b22790e4", - "sha256:a34690d8d4fc8eab34ea2dd9a99482dbbf0b1f059fe25effe204dd59bceda069", - "sha256:1577a44b1f4bbc942136bce654df24e31735e1ff6aaa37e0a6207abf13868be9", - "sha256:f68f0f13498c9350ffc712765c4e0bdc824a4afd767d1a71933ff7be380bf75e", - "sha256:76d6861664126fef4cbbdc6218ca09d81c4ed8da4c6df9637e0069f7d820f901", - "sha256:327c89477e6ea0e240cd9f94c241747a534ac6f3e71c9b2f3298485ffc8939b2", - "sha256:4f1675010fd934aea011642c33c0dd9bc6954d9be7032c7f9ccfea1ac8d752d7", - "sha256:e996c34fb101c9c6d88a839c19af74d7c067b92ed3371274efcf4d4b6dc160a6" + "sha256:0c14388546db6605a860b8b7112cb69d0b11c9ce5e072210504544e0d4575799", + "sha256:39a255776528afe11ea0d57814f9bf3729c1e0b99063af2e5c6cfd750c3e1f7f", + "sha256:3c85e8cbc91f45e1cc916cc9180b34153cd6aaaaacfb51a48b3156318314fa82", + "sha256:8199206c479b13947dcac63c025575d035331bb3819d1783dc1d568a11962906", + "sha256:8798aeca58b3dd7ffdc5a4993c9eaafedc4048165429e8f499ddd62c73bf3964", + "sha256:881efe37328de0785c0731d462e1485a45712f2cd5cb55907d6c15458460ebeb", + "sha256:bcca072f82e84b419efec1320cd3ee5c230d263f3a601b146651ed4db77d89f0", + "sha256:ff0c5fa3af4a471a218408c448b804ac6bccb105127727474f4e83c0e4072e97" ], - "version": "==1.4.0" + "index": "pypi", + "version": "==1.4.2" }, "python-dateutil": { "hashes": [ - "sha256:95511bae634d69bc7329ba55e646499a842bc4ec342ad54a8cdb65645a0aad3c", - "sha256:891c38b2a02f5bb1be3e4793866c8df49c7d19baabf9c1bad62547e0b4866aca" + "sha256:07009062406cffd554a9b4135cd2ff167c9bf6b7aac61fe946c93e69fad1bbd8", + "sha256:8f95bb7e6edbb2456a51a1fb58c8dca942024b4f5844cae62c90aa88afe6e300" ], - "version": "==2.6.1" + "version": "==2.7.0" }, "pytz": { "hashes": [ - "sha256:80af0f3008046b9975242012a985f04c5df1f01eed4ec1633d56cc47a75a6a48", - "sha256:feb2365914948b8620347784b6b6da356f31c9d03560259070b2f30cff3d469d", - "sha256:59707844a9825589878236ff2f4e0dc9958511b7ffaae94dc615da07d4a68d33", - "sha256:d0ef5ef55ed3d37854320d4926b04a4cb42a2e88f71da9ddfdacfde8e364f027", - "sha256:c41c62827ce9cafacd6f2f7018e4f83a6f1986e87bfd000b8cfbd4ab5da95f1a", - "sha256:8cc90340159b5d7ced6f2ba77694d946fc975b09f1a51d93f3ce3bb399396f94", - "sha256:dd2e4ca6ce3785c8dd342d1853dd9052b19290d5bf66060846e5dc6b8d6667f7", - "sha256:699d18a2a56f19ee5698ab1123bbcc1d269d061996aeb1eda6d89248d3542b82", - "sha256:fae4cffc040921b8a2d60c6cf0b5d662c1190fe54d718271db4eb17d44a185b7" + "sha256:07edfc3d4d2705a20a6e99d97f0c4b61c800b8232dc1c04d87e8554f130148dd", + "sha256:3a47ff71597f821cd84a162e71593004286e5be07a340fd462f0d33a760782b5", + "sha256:410bcd1d6409026fbaa65d9ed33bf6dd8b1e94a499e32168acfc7b332e4095c0", + "sha256:5bd55c744e6feaa4d599a6cbd8228b4f8f9ba96de2c38d56f08e534b3c9edf0d", + "sha256:61242a9abc626379574a166dc0e96a66cd7c3b27fc10868003fa210be4bff1c9", + "sha256:887ab5e5b32e4d0c86efddd3d055c1f363cbaa583beb8da5e22d2fa2f64d51ef", + "sha256:ba18e6a243b3625513d85239b3e49055a2f0318466e0b8a92b8fb8ca7ccdf55f", + "sha256:ed6509d9af298b7995d69a440e2822288f2eca1681b8cce37673dbb10091e5fe", + "sha256:f93ddcdd6342f94cea379c73cddb5724e0d6d0a1c91c9bdef364dc0368ba4fda" ], - "version": "==2017.3" + "index": "pypi", + "version": "==2018.3" }, "pytzdata": { "hashes": [ - "sha256:cd5b72400a7378b3b45eef5929cbe97ed44c3368685c35c477e316ebaa7e1809", - "sha256:e87376f2ee7cb89af5ddea5ed07ce3e98a55f891d07ae87d8c49e99f069423f2" + "sha256:4e2cceb54335cd6c28caea46b15cd592e2aec5e8b05b0241cbccfb1b23c02ae7", + "sha256:7cd949123e2c2060fd12793de3a4a449e36b5dea5e169b810a3ac3f0b9877cfa" ], - "version": "==2017.3.1" + "version": "==2018.3" }, "regex": { "hashes": [ - "sha256:28a542117efd479cff110711c28f34cb40636f670d427dc42710b5183fbd92c4", - "sha256:93064faef899911a7378443fcfb1f2e93e19706a324162fc71dd996c85b24f37", - "sha256:9c4b23f8a68470522a0cdfbdcffbe2f61d7659b5d505cb7c1a729a6805345210", - "sha256:45fed2f98e5ca0fc420ff31cbd9eccbee41e4d607569fddaba0faea36ef6043d", - "sha256:079c656874ba6c2e3332df488d4eff33b94b4e260e3901dce1553cb4f1f36c73", - "sha256:c1ef8f72944bef49261daaa83c7d77439f54f8deefefc0e2cf7144ac8b20ac1f", - "sha256:26795107acb81bb07ef832832548834b03846eb046e5ba9a5917ee012c68c1e6", - "sha256:3e2609e0e366b21c3db3d41159d99e7bd37e02caaba24bba77ec5cc594c62c4e", - "sha256:801e054c1aa163545d29e186a1bea779437a19b49c4da9e11049624c2d2bfd31", - "sha256:51306abdaac9e712b208066d284ddc7e3a332c77ad6054ba8d305d607609a328", - "sha256:660990e223ef2f71cb78b4e106a9a023652a31fd305051a901b0f87171b69e24", - "sha256:47230e1af3479810b1ef2bf23768b5195588a03eea6248b678d6893c48f58082", - "sha256:5f15a27c24ed4ad2ed492abe80ddf27d35d63ef6c0d8878c915de6bf9f36c6f3", - "sha256:67025161b70b0625749b1b89200da59fecc2fda9d1e46f9ef588f9a4af9fc48f", - "sha256:139678fc013b75e486e580c39b4c52d085ed7362e400960f8be1711a414f16b5" + "sha256:1b428a296531ea1642a7da48562746309c5c06471a97bd0c02dd6a82e9cecee8", + "sha256:27d72bb42dffb32516c28d218bb054ce128afd3e18464f30837166346758af67", + "sha256:32cf4743debee9ea12d3626ee21eae83052763740e04086304e7a74778bf58c9", + "sha256:32f6408dbca35040bc65f9f4ae1444d5546411fde989cb71443a182dd643305e", + "sha256:333687d9a44738c486735955993f83bd22061a416c48f5a5f9e765e90cf1b0c9", + "sha256:35eeccf17af3b017a54d754e160af597036435c58eceae60f1dd1364ae1250c7", + "sha256:361a1fd703a35580a4714ec28d85e29780081a4c399a99bbfb2aee695d72aedb", + "sha256:494bed6396a20d3aa6376bdf2d3fbb1005b8f4339558d8ac7b53256755f80303", + "sha256:5b9c0ddd5b4afa08c9074170a2ea9b34ea296e32aeea522faaaaeeeb2fe0af2e", + "sha256:a50532f61b23d4ab9d216a6214f359dd05c911c1a1ad20986b6738a782926c1a", + "sha256:a9243d7b359b72c681a2c32eaa7ace8d346b7e8ce09d172a683acf6853161d9c", + "sha256:b44624a38d07d3c954c84ad302c29f7930f4bf01443beef5589e9157b14e2a29", + "sha256:be42a601aaaeb7a317f818490a39d153952a97c40c6e9beeb2a1103616405348", + "sha256:eee4d94b1a626490fc8170ffd788883f8c641b576e11ba9b4a29c9f6623371e0", + "sha256:f69d1201a4750f763971ea8364ed95ee888fc128968b39d38883a72a4d005895" ], - "version": "==2018.1.10" + "version": "==2018.2.21" }, "ruamel.yaml": { "hashes": [ - "sha256:14d161558e3bf89e87d77c218098be22fa9a0d6d0bea40250fce525b1d0cbee2", - "sha256:fcfc24a21594c071cc4588e84b7657a1f47ebcf6037c6c43fa15c4bbd3989ec2", - "sha256:02babffd019911841ba01b76e23dfec7c9e9b2725503fb2698c4982fa1a6e835", - "sha256:c0908896e34b617ead40552cab03c1769bdc43d1da02419160dc900c5dfddde2", "sha256:01e30ecb1b1c0ebf9fce814dc20dace402571517277799291202b61b22096c24", - "sha256:b6c5d5f03ba78e3f27c7188a00c4e09b6a4507fe3154ba40a294e09cb30ee016", - "sha256:9225c83952d28f302cfc23c3d9a6f8231bfd581476d7aff1e3c7de49eecb4ee9", - "sha256:c41e04b526d0153c9246cfab87d7ddefdc9f165cb8886a8ec48ba7a2b73069f6", - "sha256:6d05c5a5baf829c70916c226ef3200650846a7227de226bca8a59efaf88bb973", - "sha256:e3bbfe0d294e08fdbb0cb05485435a2ceb4e168e98b5dc611f051c1864986b4b", - "sha256:68c8f2986bcb91b6db1aea8698941769840c7257e951a9377048f7eff35be773", + "sha256:02babffd019911841ba01b76e23dfec7c9e9b2725503fb2698c4982fa1a6e835", "sha256:072f6364a89972e8dc0afdce3335a709d5464dfeaa4f736d092a54574338b874", + "sha256:14d161558e3bf89e87d77c218098be22fa9a0d6d0bea40250fce525b1d0cbee2", "sha256:5504398fc755a2b14c9983b2101161a8591a4b30812590cc1c365e7fcc117dfa", - "sha256:e2d2715bf92156bec5fb42e92e95dac1c4d9904f8a3d4e2d0c438758fe9092d7", + "sha256:68c8f2986bcb91b6db1aea8698941769840c7257e951a9377048f7eff35be773", + "sha256:6d05c5a5baf829c70916c226ef3200650846a7227de226bca8a59efaf88bb973", "sha256:6d7929b24e329d662fa43b657fddfee5260e2d35d0a543065cd755d4e17a9b2f", + "sha256:8dc74821e4bb6b21fb1ab35964e159391d99ee44981d07d57bf96e2395f3ef75", + "sha256:9225c83952d28f302cfc23c3d9a6f8231bfd581476d7aff1e3c7de49eecb4ee9", + "sha256:b6c5d5f03ba78e3f27c7188a00c4e09b6a4507fe3154ba40a294e09cb30ee016", + "sha256:c0908896e34b617ead40552cab03c1769bdc43d1da02419160dc900c5dfddde2", + "sha256:c41e04b526d0153c9246cfab87d7ddefdc9f165cb8886a8ec48ba7a2b73069f6", + "sha256:e2d2715bf92156bec5fb42e92e95dac1c4d9904f8a3d4e2d0c438758fe9092d7", + "sha256:e3bbfe0d294e08fdbb0cb05485435a2ceb4e168e98b5dc611f051c1864986b4b", "sha256:f2d02a4af5a13b09d0b823cdd0317b54f3e0115e50b5ac4d9840c3a1b566817f", - "sha256:8dc74821e4bb6b21fb1ab35964e159391d99ee44981d07d57bf96e2395f3ef75" + "sha256:fcfc24a21594c071cc4588e84b7657a1f47ebcf6037c6c43fa15c4bbd3989ec2" ], + "index": "pypi", "version": "==0.15.35" }, "six": { "hashes": [ - "sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb", - "sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9" + "sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9", + "sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb" ], "version": "==1.11.0" }, + "snaptime": { + "hashes": [ + "sha256:e3f1eb89043d58d30721ab98cb65023f1a4c2740e3b197704298b163c92d508b" + ], + "index": "pypi", + "version": "==0.2.4" + }, "tzlocal": { "hashes": [ "sha256:4ebeb848845ac898da6519b9b31879cf13b6626f7184c496037b818e238f2c4e" ], + "index": "pypi", "version": "==1.5.1" } }, @@ -147,15 +147,15 @@ }, "attrs": { "hashes": [ - "sha256:a17a9573a6f475c99b551c0e0a812707ddda1ec9653bed04c13841404ed6f450", - "sha256:1c7960ccfd6a005cd9f7ba884e6316b5e430a3f1a6c37c5f87d8b43f83b54ec9" + "sha256:1c7960ccfd6a005cd9f7ba884e6316b5e430a3f1a6c37c5f87d8b43f83b54ec9", + "sha256:a17a9573a6f475c99b551c0e0a812707ddda1ec9653bed04c13841404ed6f450" ], "version": "==17.4.0" }, "babel": { "hashes": [ - "sha256:ad209a68d7162c4cff4b29cdebe3dec4cef75492df501b0049a9433c96ce6f80", - "sha256:8ce4cb6fdd4393edd323227cba3a077bceb2a6ce5201c902c65e730046f41f14" + "sha256:8ce4cb6fdd4393edd323227cba3a077bceb2a6ce5201c902c65e730046f41f14", + "sha256:ad209a68d7162c4cff4b29cdebe3dec4cef75492df501b0049a9433c96ce6f80" ], "version": "==2.5.3" }, @@ -168,32 +168,32 @@ }, "chardet": { "hashes": [ - "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691", - "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae" + "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", + "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" ], "version": "==3.0.4" }, "docutils": { "hashes": [ - "sha256:7a4bd47eaf6596e1295ecb11361139febe29b084a87bf005bf899f9a42edc3c6", "sha256:02aec4bd92ab067f6ff27a38a38a41173bf01bed8f89157768c1573f53e474a6", - "sha256:51e64ef2ebfb29cae1faa133b3710143496eca21c530f3f71424d77687764274" + "sha256:51e64ef2ebfb29cae1faa133b3710143496eca21c530f3f71424d77687764274", + "sha256:7a4bd47eaf6596e1295ecb11361139febe29b084a87bf005bf899f9a42edc3c6" ], "version": "==0.14" }, "idna": { "hashes": [ - "sha256:8c7309c718f94b3a625cb648ace320157ad16ff131ae0af362c9f21b80ef6ec4", - "sha256:2c6a5de3089009e3da7c5dde64a141dbc8551d5b7f6cf4ed7c2568d0cc520a8f" + "sha256:2c6a5de3089009e3da7c5dde64a141dbc8551d5b7f6cf4ed7c2568d0cc520a8f", + "sha256:8c7309c718f94b3a625cb648ace320157ad16ff131ae0af362c9f21b80ef6ec4" ], "version": "==2.6" }, "imagesize": { "hashes": [ - "sha256:6ebdc9e0ad188f9d1b2cdd9bc59cbe42bf931875e829e7a595e6b3abdc05cdfb", - "sha256:0ab2c62b87987e3252f89d30b7cedbec12a01af9274af9ffa48108f2c13c6062" + "sha256:3620cc0cadba3f7475f9940d22431fc4d407269f1be59ec9b8edcca26440cf18", + "sha256:5b326e4678b6925158ccc66a9fa3122b6106d7c876ee32d7de6ce59385b96315" ], - "version": "==0.7.1" + "version": "==1.0.0" }, "jinja2": { "hashes": [ @@ -208,6 +208,13 @@ ], "version": "==1.0" }, + "packaging": { + "hashes": [ + "sha256:e9215d2d2535d3ae866c3d6efc77d5b24a0192cce0ff20e42896cc0664f889c0", + "sha256:f019b770dd64e585a99714f1fd5e01c7a8f11b45635aa953fd41c689a657375b" + ], + "version": "==17.1" + }, "pluggy": { "hashes": [ "sha256:7f8ae7f5bdf75671a718d2daf0a64b7885f74510bcd98b1a0bb420eb9a9d0cff" @@ -228,26 +235,40 @@ ], "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": { "hashes": [ - "sha256:b84878865558194630c6147f44bdaef27222a9f153bbd4a08908b16bf285e0b1", - "sha256:53548280ede7818f4dc2ad96608b9f08ae2cc2ca3874f2ceb6f97e3583f25bc4" + "sha256:062027955bccbc04d2fcd5d79690947e018ba31abe4c90b2c6721abec734261b", + "sha256:117bad36c1a787e1a8a659df35de53ba05f9f3398fb9e4ac17e80ad5903eb8c5" ], - "version": "==3.3.2" + "index": "pypi", + "version": "==3.4.2" }, "pytz": { "hashes": [ - "sha256:80af0f3008046b9975242012a985f04c5df1f01eed4ec1633d56cc47a75a6a48", - "sha256:feb2365914948b8620347784b6b6da356f31c9d03560259070b2f30cff3d469d", - "sha256:59707844a9825589878236ff2f4e0dc9958511b7ffaae94dc615da07d4a68d33", - "sha256:d0ef5ef55ed3d37854320d4926b04a4cb42a2e88f71da9ddfdacfde8e364f027", - "sha256:c41c62827ce9cafacd6f2f7018e4f83a6f1986e87bfd000b8cfbd4ab5da95f1a", - "sha256:8cc90340159b5d7ced6f2ba77694d946fc975b09f1a51d93f3ce3bb399396f94", - "sha256:dd2e4ca6ce3785c8dd342d1853dd9052b19290d5bf66060846e5dc6b8d6667f7", - "sha256:699d18a2a56f19ee5698ab1123bbcc1d269d061996aeb1eda6d89248d3542b82", - "sha256:fae4cffc040921b8a2d60c6cf0b5d662c1190fe54d718271db4eb17d44a185b7" + "sha256:07edfc3d4d2705a20a6e99d97f0c4b61c800b8232dc1c04d87e8554f130148dd", + "sha256:3a47ff71597f821cd84a162e71593004286e5be07a340fd462f0d33a760782b5", + "sha256:410bcd1d6409026fbaa65d9ed33bf6dd8b1e94a499e32168acfc7b332e4095c0", + "sha256:5bd55c744e6feaa4d599a6cbd8228b4f8f9ba96de2c38d56f08e534b3c9edf0d", + "sha256:61242a9abc626379574a166dc0e96a66cd7c3b27fc10868003fa210be4bff1c9", + "sha256:887ab5e5b32e4d0c86efddd3d055c1f363cbaa583beb8da5e22d2fa2f64d51ef", + "sha256:ba18e6a243b3625513d85239b3e49055a2f0318466e0b8a92b8fb8ca7ccdf55f", + "sha256:ed6509d9af298b7995d69a440e2822288f2eca1681b8cce37673dbb10091e5fe", + "sha256:f93ddcdd6342f94cea379c73cddb5724e0d6d0a1c91c9bdef364dc0368ba4fda" ], - "version": "==2017.3" + "index": "pypi", + "version": "==2018.3" }, "requests": { "hashes": [ @@ -258,29 +279,30 @@ }, "six": { "hashes": [ - "sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb", - "sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9" + "sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9", + "sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb" ], "version": "==1.11.0" }, "snowballstemmer": { "hashes": [ - "sha256:9f3bcd3c401c3e862ec0ebe6d2c069ebc012ce142cce209c098ccb5b09136e89", - "sha256:919f26a68b2c17a7634da993d91339e288964f93c274f1343e3bbbe2096e1128" + "sha256:919f26a68b2c17a7634da993d91339e288964f93c274f1343e3bbbe2096e1128", + "sha256:9f3bcd3c401c3e862ec0ebe6d2c069ebc012ce142cce209c098ccb5b09136e89" ], "version": "==1.2.1" }, "sphinx": { "hashes": [ - "sha256:b8baed19394af85b21755c68c7ec4eac57e8a482ed89cd01cd5d5ff72200fe0f", - "sha256:c39a6fa41bd3ec6fc10064329a664ed3a3ca2e27640a823dc520c682e4433cdb" + "sha256:41ae26acc6130ccf6ed47e5cca73742b80d55a134f0ab897c479bba8d3640b8e", + "sha256:da987de5fcca21a4acc7f67a86a363039e67ac3e8827161e61b91deb131c0ee8" ], - "version": "==1.6.6" + "index": "pypi", + "version": "==1.7.1" }, "sphinxcontrib-websupport": { "hashes": [ - "sha256:f4932e95869599b89bf4f80fc3989132d83c9faa5bf633e7b5e0c25dffb75da2", - "sha256:7a85961326aa3a400cd4ad3c816d70ed6f7c740acd7ce5d78cd0a67825072eb9" + "sha256:7a85961326aa3a400cd4ad3c816d70ed6f7c740acd7ce5d78cd0a67825072eb9", + "sha256:f4932e95869599b89bf4f80fc3989132d83c9faa5bf633e7b5e0c25dffb75da2" ], "version": "==1.0.1" }, diff --git a/README.rst b/README.rst index 91ded5b..02483d5 100644 --- a/README.rst +++ b/README.rst @@ -69,10 +69,10 @@ Behold, datetimes for humans! >>> m = maya.MayaDT.from_datetime(datetime.utcnow()) >>> print(m) Wed, 20 Sep 2017 17:24:32 GMT - + >>> m = maya.MayaDT.from_struct(time.gmtime()) >>> print(m) - Wed, 20 Sep 2017 17:24:32 GMT + Wed, 20 Sep 2017 17:24:32 GMT >>> rand_day.day 7 @@ -88,10 +88,15 @@ Behold, datetimes for humans! >>> maya.intervals(start=maya.now(), end=maya.now().add(days=1), interval=60*60) + # snap modifiers + >>> dt = maya.when('Mon, 21 Feb 1994 21:21:42 GMT') + >>> dt.snap('@d+3h').rfc2822() + 'Mon, 21 Feb 1994 03:00:00 GMT' + ☤ Advanced Usage of Maya ------------------------ -In addition to timestamps, Maya also includes a wonderfuly powerful ``MayaInterval`` class, which represents a range of time (e.g. an event). With this class, you can perform a multitude of advanced calendar calculations with finesse and ease. +In addition to timestamps, Maya also includes a wonderfully powerful ``MayaInterval`` class, which represents a range of time (e.g. an event). With this class, you can perform a multitude of advanced calendar calculations with finesse and ease. For example: @@ -99,15 +104,15 @@ For example: >>> from maya import MayaInterval - # Create an event that is one hour long, starting now. + # Create an event that is one hour long, starting now. >>> event_start = maya.now() >>> event_end = event_start.add(hours=1) - + >>> event = MayaInterval(start=event_start, end=event_end) From here, there are a number of methods available to you, which you can use to compare this event to another event. - - + + ☤ Why is this useful? --------------------- @@ -124,7 +129,7 @@ From here, there are a number of methods available to you, which you can use to ☤ What about Delorean, Arrow, & Pendulum? ----------------------------------------- -All these project complement each other, and are friends. Pendulum, for example, helps power Maya's parsing. +All these project complement each other, and are friends. Pendulum, for example, helps power Maya's parsing. Arrow, for example, is a fantastic library, but isn't what I wanted in a datetime library. In many ways, it's better than Maya for certain things. In some ways, in my opinion, it's not. diff --git a/maya/core.py b/maya/core.py index 17db01f..22b6b00 100644 --- a/maya/core.py +++ b/maya/core.py @@ -17,6 +17,7 @@ import pytz import humanize import dateparser import pendulum +import snaptime from tzlocal import get_localzone from .compat import cmp, comparable @@ -138,6 +139,15 @@ class MayaDT(object): """Returns a timedelta object with the duration between the dates""" 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 # ------------- @@ -261,7 +271,7 @@ class MayaDT(object): @property def day(self): return self.datetime().day - + @property def date(self): return self.datetime().date() diff --git a/setup.py b/setup.py index fcab82f..701f41b 100755 --- a/setup.py +++ b/setup.py @@ -26,7 +26,8 @@ required = [ 'dateparser', 'ruamel.yaml', 'tzlocal', - 'pendulum' + 'pendulum', + 'snaptime' ] packages = [ diff --git a/tests/test_maya.py b/tests/test_maya.py index 6eea720..5c08b53 100644 --- a/tests/test_maya.py +++ b/tests/test_maya.py @@ -314,3 +314,14 @@ def test_core_local_timezone(monkeypatch): monkeypatch.setattr(maya.MayaDT, '_local_tz', mock_local_tz) mdt = maya.MayaDT(0) 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') From 9a850b4212f3345fdc0d726732fa9186c274a204 Mon Sep 17 00:00:00 2001 From: Timo Furrer Date: Sat, 17 Mar 2018 12:21:01 +0100 Subject: [PATCH 4/4] PEP8 formatting by white Used `white maya/ tests/`. --- maya/compat.py | 20 +- maya/core.py | 222 ++++++++++++--------- tests/test_maya.py | 67 ++----- tests/test_maya_interval.py | 379 ++++++++++++++++-------------------- 4 files changed, 326 insertions(+), 362 deletions(-) diff --git a/maya/compat.py b/maya/compat.py index b855720..abda9a7 100644 --- a/maya/compat.py +++ b/maya/compat.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- - """ maya.compat ~~~~~~~~~~~~~~~ @@ -12,24 +11,19 @@ import sys # ------- # Pythons # ------- - # Syntax sugar. _ver = sys.version_info - -#: Python 2.x? +# : Python 2.x? is_py2 = (_ver[0] == 2) - -#: Python 3.x? +# : Python 3.x? is_py3 = (_ver[0] == 3) - # --------- # Specifics # --------- - if is_py2: cmp = cmp - elif is_py3: + def cmp(a, b): """ Compare two objects. @@ -38,8 +32,10 @@ elif is_py3: """ if a < b: return -1 + elif a == b: return 0 + else: return 1 @@ -59,36 +55,42 @@ def comparable(klass): c = self.__cmp__(other) if c is NotImplemented: return c + return c == 0 def __ne__(self, other): c = self.__cmp__(other) if c is NotImplemented: return c + return c != 0 def __lt__(self, other): c = self.__cmp__(other) if c is NotImplemented: return c + return c < 0 def __le__(self, other): c = self.__cmp__(other) if c is NotImplemented: return c + return c <= 0 def __gt__(self, other): c = self.__cmp__(other) if c is NotImplemented: return c + return c > 0 def __ge__(self, other): c = self.__cmp__(other) if c is NotImplemented: return c + return c >= 0 klass.__lt__ = __lt__ diff --git a/maya/core.py b/maya/core.py index 22b6b00..1dafc61 100644 --- a/maya/core.py +++ b/maya/core.py @@ -1,7 +1,6 @@ # ___ __ ___ _ _ ___ # || \/ | ||=|| \\// ||=|| # || | || || // || || - # Ignore warnings for yaml usage. import warnings import ruamel.yaml @@ -10,9 +9,9 @@ warnings.simplefilter('ignore', ruamel.yaml.error.UnsafeLoaderWarning) import email.utils import time +import functools from datetime import timedelta, datetime as Datetime -import functools import pytz import humanize import dateparser @@ -32,14 +31,20 @@ def validate_class_type_arguments(operator): """ def inner(function): + def wrapper(self, *args, **kwargs): for arg in args + tuple(kwargs.values()): if not isinstance(arg, self.__class__): - raise TypeError('unorderable types: {}() {} {}()'.format( - type(self).__name__, operator, type(arg).__name__)) + raise TypeError( + 'unorderable types: {}() {} {}()'.format( + type(self).__name__, operator, type(arg).__name__ + ) + ) + return function(self, *args, **kwargs) return wrapper + return inner @@ -52,21 +57,28 @@ def validate_arguments_type_of_function(param_type=None): Note: Use this decorator on the functions of the class. """ + def inner(function): + def wrapper(self, *args, **kwargs): type_ = param_type or type(self) for arg in args + tuple(kwargs.values()): if not isinstance(arg, type_): - raise TypeError(('Invalid Type: {}.{}() accepts only the ' - 'arguments of type "<{}>"').format( - type(self).__name__, - function.__name__, - type_.__name__, - ) - ) + raise TypeError( + ( + 'Invalid Type: {}.{}() accepts only the ' + 'arguments of type "<{}>"' + ).format( + type(self).__name__, + function.__name__, + type_.__name__, + ) + ) + return function(self, *args, **kwargs) return wrapper + return inner @@ -115,7 +127,9 @@ class MayaDT(object): return hash(int(self.epoch)) def __add__(self, duration): - return self.add(seconds=_seconds_or_timedelta(duration).total_seconds()) + return self.add( + seconds=_seconds_or_timedelta(duration).total_seconds() + ) def __radd__(self, duration): return self + duration @@ -123,17 +137,23 @@ class MayaDT(object): def __sub__(self, duration_or_date): if isinstance(duration_or_date, MayaDT): return self.subtract_date(dt=duration_or_date) + else: return self.subtract( - seconds=_seconds_or_timedelta(duration_or_date).total_seconds()) + seconds=_seconds_or_timedelta(duration_or_date).total_seconds() + ) def add(self, **kwargs): """Returns a new MayaDT object with the given offsets.""" - return self.from_datetime(pendulum.instance(self.datetime()).add(**kwargs)) + return self.from_datetime( + pendulum.instance(self.datetime()).add(**kwargs) + ) def subtract(self, **kwargs): """Returns a new MayaDT object with the given offsets.""" - return self.from_datetime(pendulum.instance(self.datetime()).subtract(**kwargs)) + return self.from_datetime( + pendulum.instance(self.datetime()).subtract(**kwargs) + ) def subtract_date(self, **kwargs): """Returns a timedelta object with the duration between the dates""" @@ -150,7 +170,6 @@ class MayaDT(object): # Timezone Crap # ------------- - @property def timezone(self): """Returns the UTC tzinfo name. It's always UTC. Always.""" @@ -163,9 +182,10 @@ class MayaDT(object): @property def local_timezone(self): - """Returns the name of the local timezone, for informational purposes.""" + """Returns the name of the local timezone.""" if self._local_tz.zone in pytz.all_timezones: return self._local_tz.zone + return self.timezone @property @@ -177,17 +197,14 @@ class MayaDT(object): @validate_arguments_type_of_function(Datetime) def __dt_to_epoch(dt): """Converts a datetime into an epoch.""" - # Assume UTC if no datetime is provided. if dt.tzinfo is None: dt = dt.replace(tzinfo=pytz.utc) - epoch_start = Datetime(*_EPOCH_START, tzinfo=pytz.timezone('UTC')) return (dt - epoch_start).total_seconds() # Importers # --------- - @classmethod @validate_arguments_type_of_function(Datetime) def from_datetime(klass, dt): @@ -197,7 +214,10 @@ class MayaDT(object): @classmethod @validate_arguments_type_of_function(time.struct_time) def from_struct(klass, struct, timezone=pytz.UTC): - """Returns MayaDT instance from a 9-tuple struct (assumed to be from gmtime())""" + """Returns MayaDT instance from a 9-tuple struct + + It's assumed to be from gmtime(). + """ struct_time = time.mktime(struct) - utc_offset() dt = Datetime.fromtimestamp(struct_time, timezone) return klass(klass.__dt_to_epoch(dt)) @@ -219,28 +239,27 @@ class MayaDT(object): # Exporters # --------- - def datetime(self, to_timezone=None, naive=False): """Returns a timezone-aware datetime... Defaulting to UTC (as it should). Keyword Arguments: - to_timezone {string} -- timezone to convert to (default: None/UTC) - naive {boolean} -- if True, the tzinfo is simply dropped (default: False) + to_timezone {str} -- timezone to convert to (default: None/UTC) + naive {bool} -- if True, + the tzinfo is simply dropped (default: False) """ if to_timezone: dt = self.datetime().astimezone(pytz.timezone(to_timezone)) else: dt = Datetime.utcfromtimestamp(self._epoch) dt.replace(tzinfo=self._tz) - # Strip the timezone info if requested to do so. if naive: return dt.replace(tzinfo=None) + else: if dt.tzinfo is None: dt = dt.replace(tzinfo=self._tz) - return dt def iso8601(self): @@ -259,7 +278,6 @@ class MayaDT(object): # Properties # ---------- - @property def year(self): return self.datetime().year @@ -282,7 +300,10 @@ class MayaDT(object): @property def weekday(self): - """Return the day of the week as an integer. Monday is 1 and Sunday is 7""" + """Return the day of the week as an integer. + + Monday is 1 and Sunday is 7. + """ return self.datetime().isoweekday() @property @@ -307,7 +328,6 @@ class MayaDT(object): # Human Slang Extras # ------------------ - def slang_date(self): """"Returns human slang representation of date.""" dt = self.datetime(naive=True, to_timezone=self.local_timezone) @@ -332,12 +352,14 @@ def utc_offset(): def to_utc_offset_naive(dt): if dt.tzinfo is None: return dt + return dt.astimezone(pytz.utc).replace(tzinfo=None) def to_utc_offset_aware(dt): if dt.tzinfo is not None: return dt + return pytz.utc.localize(dt) @@ -346,40 +368,46 @@ def to_iso8601(dt): def end_of_day_midnight(dt): - return dt if dt.time() == time.min else \ - (dt.replace(hour=0, minute=0, second=0, microsecond=0) + timedelta(days=1)) + if dt.time() == time.min: + return dt + + else: + return ( + dt.replace(hour=0, minute=0, second=0, microsecond=0) + + timedelta(days=1) + ) @comparable class MayaInterval(object): """ - A MayaInterval represents a range between two datetimes, inclusive of the start - and exclusive of the end. + A MayaInterval represents a range between two datetimes, + inclusive of the start and exclusive of the end. """ def __init__(self, start=None, end=None, duration=None): try: # Ensure that proper arguments were passed. - assert any(( - (start and end), - (start and duration is not None), - (end and duration is not None), - )) + assert any( + ( + (start and end), + (start and duration is not None), + (end and duration is not None), + ) + ) assert not all((start, end, duration is not None)) except AssertionError: raise ValueError( - 'Exactly 2 of start, end, and duration must be specified') + 'Exactly 2 of start, end, and duration must be specified' + ) # Convert duration to timedelta if seconds were provided. if duration: duration = _seconds_or_timedelta(duration) - if not start: start = end - duration - if not end: end = start + duration - if start > end: raise ValueError('MayaInterval cannot end before it starts') @@ -387,7 +415,9 @@ class MayaInterval(object): self.end = end def __repr__(self): - return ''.format(self.start, self.end) + return ''.format( + self.start, self.end + ) def iso8601(self): """Returns an ISO 8601 representation of the MayaInterval.""" @@ -405,7 +435,6 @@ class MayaInterval(object): # end = parse(start) # except pendulum.parsing.exceptions.ParserError as e: # end = self._parse_iso8601_duration(start) - # # Start and duration, such as "2007-03-01T13:00:00Z/P1Y2M10DT2H30M" # # Duration and end, such as "P1Y2M10DT2H30M/2008-05-11T15:30:00Z" raise NotImplementedError() @@ -421,8 +450,7 @@ class MayaInterval(object): @validate_arguments_type_of_function() def __eq__(self, maya_interval): return ( - self.start == maya_interval.start and - self.end == maya_interval.end + self.start == maya_interval.start and self.end == maya_interval.end ) def __hash__(self): @@ -435,8 +463,8 @@ class MayaInterval(object): @validate_arguments_type_of_function() def __cmp__(self, maya_interval): return ( - cmp(self.start, maya_interval.start) or - cmp(self.end, maya_interval.end) + cmp(self.start, maya_interval.start) + or cmp(self.end, maya_interval.end) ) @property @@ -467,8 +495,9 @@ class MayaInterval(object): MayaInterval( interval_list[0].start, max(interval_list[0].end, interval_list[1].end), - ), + ) ] + return interval_list @validate_arguments_type_of_function() @@ -476,6 +505,7 @@ class MayaInterval(object): """"Removes the given interval.""" if not self & maya_interval: return [self] + elif maya_interval.contains(self): return [] @@ -487,10 +517,8 @@ class MayaInterval(object): return interval_list def split(self, duration, include_remainder=True): - # Convert seconds to timedelta, if appropriate. duration = _seconds_or_timedelta(duration) - if duration <= timedelta(seconds=0): raise ValueError('cannot call split with a non-positive timedelta') @@ -498,38 +526,36 @@ class MayaInterval(object): while start < self.end: if start + duration <= self.end: yield MayaInterval(start, start + duration) + elif include_remainder: yield MayaInterval(start, self.end) + start += duration def quantize(self, duration, snap_out=False, timezone='UTC'): """Returns a quanitzed interval.""" - # Convert seconds to timedelta, if appropriate. duration = _seconds_or_timedelta(duration) timezone = pytz.timezone(timezone) - - if duration <= timedelta(seconds=0): raise ValueError('cannot quantize by non-positive timedelta') epoch = timezone.localize(Datetime(1970, 1, 1)) seconds = int(duration.total_seconds()) - - start_seconds = int((self.start.datetime(naive=False) - epoch).total_seconds()) - end_seconds = int((self.end.datetime(naive=False) - epoch).total_seconds()) - + start_seconds = int( + (self.start.datetime(naive=False) - epoch).total_seconds() + ) + end_seconds = int( + (self.end.datetime(naive=False) - epoch).total_seconds() + ) if start_seconds % seconds and not snap_out: start_seconds += seconds if end_seconds % seconds and snap_out: end_seconds += seconds - start_seconds -= start_seconds % seconds end_seconds -= end_seconds % seconds - if start_seconds > end_seconds: start_seconds = end_seconds - return MayaInterval( start=MayaDT.from_datetime(epoch).add(seconds=start_seconds), end=MayaDT.from_datetime(epoch).add(seconds=end_seconds), @@ -538,23 +564,17 @@ class MayaInterval(object): @validate_arguments_type_of_function() def intersection(self, maya_interval): """Returns the intersection between two intervals.""" - start = max(self.start, maya_interval.start) end = min(self.end, maya_interval.end) - either_instant = self.is_instant or maya_interval.is_instant - instant_overlap = ( - self.start == maya_interval.start or - start <= end - ) + instant_overlap = (self.start == maya_interval.start or start <= end) if (either_instant and instant_overlap) or (start < end): return MayaInterval(start, end) @validate_arguments_type_of_function() def contains(self, maya_interval): return ( - self.start <= maya_interval.start and - self.end >= maya_interval.end + self.start <= maya_interval.start and self.end >= maya_interval.end ) def __contains__(self, maya_dt): @@ -569,8 +589,7 @@ class MayaInterval(object): @validate_arguments_type_of_function() def is_adjacent(self, maya_interval): return ( - self.start == maya_interval.end or - self.end == maya_interval.start + self.start == maya_interval.end or self.end == maya_interval.start ) @property @@ -587,14 +606,28 @@ class MayaInterval(object): """.format( self.start.datetime().strftime(ical_dt_format), self.end.datetime().strftime(ical_dt_format), - ).replace(' ', '').strip('\r\n').replace('\n', '\r\n') + ).replace( + ' ', '' + ).strip( + '\r\n' + ).replace( + '\n', '\r\n' + ) @staticmethod def flatten(interval_list): - return functools.reduce(lambda reduced, maya_interval: ( - (reduced[:-1] + maya_interval.combine(reduced[-1])) - if reduced else [maya_interval] - ), sorted(interval_list), []) + return functools.reduce( + lambda reduced, + maya_interval: ( + ( + reduced[:-1] + maya_interval.combine(reduced[-1]) + ) if reduced else [ + maya_interval + ] + ), + sorted(interval_list), + [], + ) @classmethod def from_datetime(cls, start_dt=None, end_dt=None, duration=None): @@ -623,13 +656,14 @@ def when(string, timezone='UTC', prefer_past=False): 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: settings['PREFER_DATES_FROM'] = 'past' - dt = dateparser.parse(string, settings=settings) - if dt is None: raise ValueError('invalid datetime input specified.') @@ -639,20 +673,23 @@ def when(string, timezone='UTC', prefer_past=False): def parse(string, timezone='UTC', day_first=False, year_first=True): """"Returns a MayaDT instance for the machine-produced moment specified. - Powered by pendulum. Accepts most known formats. Useful for working with data. + Powered by pendulum. + Accepts most known formats. Useful for working with data. Keyword Arguments: string -- string to be parsed timezone -- timezone referenced from (default: 'UTC') - day_first -- if true, the first value (e.g. 01/05/2016) is parsed as day. - if year_first is set to True, this distinguishes between YDM and YMD. (default: False) - year_first -- if true, the first value (e.g. 2016/05/01) is parsed as year (default: True) + day_first -- if true, the first value (e.g. 01/05/2016) + is parsed as day. + if year_first is set to True, this distinguishes + between YDM and YMD. (default: False) + year_first -- if true, the first value (e.g. 2016/05/01) + is parsed as year (default: True) """ options = {} options['tz'] = timezone options['day_first'] = day_first options['year_first'] = year_first - dt = pendulum.parse(string, **options) return MayaDT.from_datetime(dt) @@ -668,17 +705,22 @@ def _seconds_or_timedelta(duration): elif isinstance(duration, timedelta): dt_timedelta = duration else: - raise TypeError('Expects argument as `datetime.timedelta` object ' - 'or seconds in `int` format') + raise TypeError( + 'Expects argument as `datetime.timedelta` object ' + 'or seconds in `int` format' + ) + return dt_timedelta def intervals(start, end, interval): - """Yields MayaDT objects between the start and end MayaDTs given, at a given interval (seconds or timedelta).""" - + """ + Yields MayaDT objects between the start and end MayaDTs given, + at a given interval (seconds or timedelta). + """ interval = _seconds_or_timedelta(interval) - current_timestamp = start while current_timestamp.epoch < end.epoch: yield current_timestamp + current_timestamp = current_timestamp.add(seconds=interval.seconds) diff --git a/tests/test_maya.py b/tests/test_maya.py index 5c08b53..55fce05 100644 --- a/tests/test_maya.py +++ b/tests/test_maya.py @@ -1,8 +1,9 @@ -import pytest -import pytz import copy import time -from datetime import date, timedelta, datetime as Datetime +from datetime import timedelta, datetime as Datetime + +import pytz +import pytest import maya from maya.core import _seconds_or_timedelta # import private function @@ -26,43 +27,30 @@ def test_parse_iso8601(): string = '20161001T1430.4+05:30' expected = '2016-10-01T09:00:00.400000Z' d = maya.MayaDT.from_iso8601(string) - assert expected == d.iso8601() - string = '2016T14' expected = '2016-01-01T14:00:00Z' d = maya.MayaDT.from_iso8601(string) - assert expected == d.iso8601() - string = '2016-10T14' expected = '2016-10-01T14:00:00Z' d = maya.MayaDT.from_iso8601(string) - assert expected == d.iso8601() - string = '2012W05' expected = '2012-01-30T00:00:00Z' d = maya.MayaDT.from_iso8601(string) - assert expected == d.iso8601() - string = '2012W055' expected = '2012-02-03T00:00:00Z' d = maya.MayaDT.from_iso8601(string) - assert expected == d.iso8601() - string = '2012007' expected = '2012-01-07T00:00:00Z' d = maya.MayaDT.from_iso8601(string) - assert expected == d.iso8601() - string = '2016-W07T09' expected = '2016-02-15T09:00:00Z' d = maya.MayaDT.from_iso8601(string) - assert expected == d.iso8601() @@ -71,13 +59,14 @@ def test_struct(): ts = time.gmtime(now) m = maya.MayaDT.from_struct(ts) dt = Datetime.fromtimestamp(now, pytz.UTC) - assert m._epoch != None + assert m._epoch is not None assert m.datetime() == dt - ts = time.localtime(now) m = maya.MayaDT.from_struct(ts) - dt = Datetime.fromtimestamp(time.mktime(ts) - maya.core.utc_offset(), pytz.UTC) - assert m._epoch != None + dt = Datetime.fromtimestamp( + time.mktime(ts) - maya.core.utc_offset(), pytz.UTC + ) + assert m._epoch is not None assert m.datetime() == dt @@ -94,14 +83,12 @@ def test_issue_104(): def test_human_when(): r1 = maya.when('yesterday') r2 = maya.when('today') - assert (r2.day - r1.day) in (1, -30, -29, -28, -27) def test_machine_parse(): r1 = maya.parse('August 14, 2015') assert r1.day == 14 - r2 = maya.parse('August 15, 2015') assert r2.day == 15 @@ -115,14 +102,12 @@ def test_dt_tz_translation(): def test_dt_tz_naive(): d1 = maya.now().datetime(naive=True) assert d1.tzinfo is None - d2 = maya.now().datetime(to_timezone='EST', naive=True) assert d2.tzinfo is None assert (d1.hour - d2.hour) % 24 == 5 def test_random_date(): - # Test properties for maya.when() d1 = maya.when('11-17-11 08:09:10') assert d1.year == 2011 @@ -134,7 +119,6 @@ def test_random_date(): assert d1.minute == 9 assert d1.second == 10 assert d1.microsecond == 0 - # Test properties for maya.parse() d2 = maya.parse('February 29, 1992 13:12:34') assert d2.year == 1992 @@ -150,10 +134,8 @@ def test_random_date(): def test_print_date(capsys): d = maya.when('11-17-11') - print(d) out, err = capsys.readouterr() - assert out == 'Thu, 17 Nov 2011 00:00:00 GMT\n' assert repr(d) == '' @@ -176,22 +158,16 @@ def test_slang_time(): def test_parse(): d = maya.parse('February 21, 1994') assert format(d) == '1994-02-21 00:00:00+00:00' - d = maya.parse('01/05/2016') assert format(d) == '2016-01-05 00:00:00+00:00' - d = maya.parse('01/05/2016', day_first=True) assert format(d) == '2016-05-01 00:00:00+00:00' - d = maya.parse('2016/05/01', year_first=True, day_first=False) assert format(d) == '2016-05-01 00:00:00+00:00' - d = maya.parse('2016/01/05', year_first=True, day_first=True) assert format(d) == '2016-05-01 00:00:00+00:00' - d = maya.parse('01/05/2016', timezone='UTC') assert format(d) == '2016-01-05 00:00:00+00:00' - d = maya.parse('01/05/2016', timezone='US/Central') assert format(d) == '2016-01-05 06:00:00+00:00' @@ -200,10 +176,8 @@ def test_when_past(): next_month = str(maya.now().add(months=1).month) this_year = maya.now().year last_year = this_year - 1 - future_date = maya.when(next_month) past_date = maya.when(next_month, prefer_past=True) - assert future_date.year == this_year if next_month == '1': assert past_date.year == this_year @@ -217,7 +191,7 @@ def test_datetime_to_timezone(): def test_rfc3339(): - mdt = maya.when('2016-01-01') + mdt = maya.when('2016-01-01') out = mdt.rfc3339() mdt2 = maya.MayaDT.from_rfc3339(out) assert mdt.epoch == mdt2.epoch @@ -227,25 +201,18 @@ def test_comparison_operations(): now = maya.now() now_copy = copy.deepcopy(now) tomorrow = maya.when('tomorrow') - assert (now == now_copy) is True assert (now == tomorrow) is False - assert (now != now_copy) is False assert (now != tomorrow) is True - assert (now < now_copy) is False assert (now < tomorrow) is True - assert (now <= now_copy) is True assert (now <= tomorrow) is True - assert (now > now_copy) is False assert (now > tomorrow) is False - assert (now >= now_copy) is True assert (now >= tomorrow) is False - # Check Exceptions with pytest.raises(TypeError): now == 1 @@ -274,8 +241,7 @@ def test_seconds_or_timedelta(): def test_intervals(): now = maya.now() tomorrow = now.add(days=1) - - assert len(list(maya.intervals(now, tomorrow, 60*60))) == 24 + assert len(list(maya.intervals(now, tomorrow, 60 * 60))) == 24 def test_dunder_add(): @@ -299,18 +265,23 @@ def test_dunder_sub(): def test_mayaDT_sub(): now = maya.now() then = now.add(days=1) - assert then - now == timedelta(24*60*60) - assert now - then == timedelta(-24*60*60) + assert then - now == timedelta(24 * 60 * 60) + assert now - then == timedelta(-24 * 60 * 60) def test_core_local_timezone(monkeypatch): + @property def mock_local_tz(self): + class StaticTzInfo(object): zone = 'local' + def __repr__(self): return "" + return StaticTzInfo() + monkeypatch.setattr(maya.MayaDT, '_local_tz', mock_local_tz) mdt = maya.MayaDT(0) assert mdt.local_timezone == 'UTC' @@ -319,9 +290,7 @@ def test_core_local_timezone(monkeypatch): 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') diff --git a/tests/test_maya_interval.py b/tests/test_maya_interval.py index 6325ef5..6286023 100755 --- a/tests/test_maya_interval.py +++ b/tests/test_maya_interval.py @@ -15,26 +15,20 @@ Melbourne = pytz.timezone('Australia/Melbourne') def test_interval_requires_2_of_start_end_duration(): start = maya.now() end = start.add(hours=1) - with pytest.raises(ValueError): maya.MayaInterval(start=start) - with pytest.raises(ValueError): maya.MayaInterval(end=end) - with pytest.raises(ValueError): maya.MayaInterval(duration=60) - with pytest.raises(ValueError): maya.MayaInterval(start=start, end=end, duration=60) - maya.MayaInterval(start=start, end=end) maya.MayaInterval(start=start, duration=60) maya.MayaInterval(end=end, duration=60) def test_interval_requires_end_time_after_or_on_start_time(): - with pytest.raises(ValueError): maya.MayaInterval(start=maya.now(), duration=0) maya.MayaInterval(start=maya.now(), duration=-1) @@ -64,7 +58,9 @@ def test_interval_init_end_duration(): assert interval.start == end.subtract(seconds=duration) -@pytest.mark.parametrize('start_doy1,end_doy1,start_doy2,end_doy2,intersection_doys', ( +@pytest.mark.parametrize( + 'start_doy1,end_doy1,start_doy2,end_doy2,intersection_doys', + ( (0, 2, 1, 3, (1, 2)), (0, 2, 3, 4, None), (0, 2, 2, 3, None), @@ -76,7 +72,8 @@ def test_interval_init_end_duration(): (1, 3, 1, 1, (1, 1)), (2, 3, 1, 1, None), (1, 3, 2, 2, (2, 2)), -), ids=( + ), + ids=( 'overlapping', 'non-overlapping', 'adjacent', @@ -87,21 +84,19 @@ def test_interval_init_end_duration(): 'instant overlapping', 'instant overlapping start only (left)', 'instant disjoint (left)', - 'instant overlapping (left)' -)) + 'instant overlapping (left)', + ), +) def test_interval_intersection( - start_doy1, end_doy1, start_doy2, end_doy2, intersection_doys + start_doy1, end_doy1, start_doy2, end_doy2, intersection_doys ): base = maya.MayaDT.from_datetime(datetime(2016, 1, 1)) interval1 = maya.MayaInterval( - base.add(days=start_doy1), - base.add(days=end_doy1), + base.add(days=start_doy1), base.add(days=end_doy1) ) interval2 = maya.MayaInterval( - base.add(days=start_doy2), - base.add(days=end_doy2), + base.add(days=start_doy2), base.add(days=end_doy2) ) - if intersection_doys: start_doy_intersection, end_doy_intersection = intersection_doys assert interval1 & interval2 == maya.MayaInterval( @@ -110,7 +105,6 @@ def test_interval_intersection( ) else: assert (interval1 & interval2) is None - # check invalid argument with pytest.raises(TypeError): interval1 & 'invalid type' @@ -119,13 +113,10 @@ def test_interval_intersection( def test_interval_intersects(): base = maya.MayaDT.from_datetime(datetime(2016, 1, 1)) interval = maya.MayaInterval(base, base.add(days=1)) - assert interval.intersects(interval) - assert not interval.intersects(maya.MayaInterval( - base.add(days=2), - base.add(days=3), - )) - + assert not interval.intersects( + maya.MayaInterval(base.add(days=2), base.add(days=3)) + ) # check invalid argument with pytest.raises(TypeError): interval.intersects('invalid type') @@ -135,13 +126,11 @@ def test_and_operator(): base = maya.MayaDT.from_datetime(datetime(2016, 1, 1)) interval1 = maya.MayaInterval(base, base.add(days=2)) interval2 = maya.MayaInterval(base.add(days=1), base.add(days=3)) - assert ( interval1 & interval2 == interval2 & interval1 == interval1.intersection(interval2) ) - # check invalid argument with pytest.raises(TypeError): interval1.intersection('invalid type') @@ -151,14 +140,11 @@ def test_interval_eq_operator(): start = maya.now() end = start.add(hours=1) interval = maya.MayaInterval(start=start, end=end) - assert interval == maya.MayaInterval(start=start, end=end) assert interval != maya.MayaInterval(start=start, end=end.add(days=1)) - # check invalid argument with pytest.raises(TypeError): interval == 'invalid type' - with pytest.raises(TypeError): interval != 'invalid type' @@ -167,7 +153,6 @@ def test_interval_timedelta(): start = maya.now() delta = timedelta(hours=1) interval = maya.MayaInterval(start=start, duration=delta) - assert interval.timedelta == delta @@ -175,69 +160,55 @@ def test_interval_duration(): start = maya.now() delta = timedelta(hours=1) interval = maya.MayaInterval(start=start, duration=delta) - assert interval.duration == delta.total_seconds() -@pytest.mark.parametrize('start_doy1,end_doy1,start_doy2,end_doy2,expected', ( +@pytest.mark.parametrize( + 'start_doy1,end_doy1,start_doy2,end_doy2,expected', + ( (0, 2, 1, 3, False), (0, 2, 3, 4, False), (0, 2, 2, 3, False), (0, 1, 0, 1, True), (0, 3, 1, 2, True), -), ids=( - 'overlapping', - 'non-overlapping', - 'adjacent', - 'equal', - 'subset', -)) + ), + ids=('overlapping', 'non-overlapping', 'adjacent', 'equal', 'subset'), +) def test_interval_contains( - start_doy1, end_doy1, start_doy2, end_doy2, expected + start_doy1, end_doy1, start_doy2, end_doy2, expected ): base = maya.MayaDT.from_datetime(datetime(2016, 1, 1)) interval1 = maya.MayaInterval( - base.add(days=start_doy1), - base.add(days=end_doy1), + base.add(days=start_doy1), base.add(days=end_doy1) ) interval2 = maya.MayaInterval( - base.add(days=start_doy2), - base.add(days=end_doy2), + base.add(days=start_doy2), base.add(days=end_doy2) ) - assert interval1.contains(interval2) is expected assert (interval2 in interval1) is expected - # check invalid argument with pytest.raises(TypeError): interval1.contains('invalid type') -@pytest.mark.parametrize('start_doy,end_doy,dt_doy,expected', ( +@pytest.mark.parametrize( + 'start_doy,end_doy,dt_doy,expected', + ( (2, 4, 1, False), (2, 4, 2, True), (2, 4, 3, True), (2, 4, 4, False), (2, 4, 5, False), -), ids=( - 'before-start', - 'on-start', - 'during', - 'on-end', - 'after-end', -)) -def test_interval_in_operator_maya_dt( - start_doy, end_doy, dt_doy, expected -): + ), + ids=('before-start', 'on-start', 'during', 'on-end', 'after-end'), +) +def test_interval_in_operator_maya_dt(start_doy, end_doy, dt_doy, expected): base = maya.MayaDT.from_datetime(datetime(2016, 1, 1)) interval = maya.MayaInterval( - start=base.add(days=start_doy), - end=base.add(days=end_doy), + start=base.add(days=start_doy), end=base.add(days=end_doy) ) dt = base.add(days=dt_doy) - assert (dt in interval) is expected - # check invalid argument with pytest.raises(TypeError): 'invalid type' in interval @@ -247,91 +218,84 @@ def test_interval_hash(): start = maya.now() end = start.add(hours=1) interval = maya.MayaInterval(start=start, end=end) - assert hash(interval) == hash(maya.MayaInterval(start=start, end=end)) - assert hash(interval) != hash(maya.MayaInterval( - start=start, end=end.add(days=1))) + assert hash(interval) != hash( + maya.MayaInterval(start=start, end=end.add(days=1)) + ) def test_interval_iter(): start = maya.now() end = start.add(days=1) - assert tuple(maya.MayaInterval(start=start, end=end)) == (start, end) -@pytest.mark.parametrize('start1,end1,start2,end2,expected', [ - (1, 2, 1, 2, 0), - (1, 3, 2, 4, -1), - (2, 4, 1, 3, 1), - (1, 2, 1, 3, -1), -], ids=( +@pytest.mark.parametrize( + 'start1,end1,start2,end2,expected', + [(1, 2, 1, 2, 0), (1, 3, 2, 4, -1), (2, 4, 1, 3, 1), (1, 2, 1, 3, -1)], + ids=( 'equal', 'less-than', 'greater-than', 'use-end-time-if-start-time-identical', -)) + ), +) def test_interval_cmp(start1, end1, start2, end2, expected): base = maya.now() interval1 = maya.MayaInterval( - start=base.add(days=start1), - end=base.add(days=end1), + start=base.add(days=start1), end=base.add(days=end1) ) interval2 = maya.MayaInterval( - start=base.add(days=start2), - end=base.add(days=end2), + start=base.add(days=start2), end=base.add(days=end2) ) assert cmp(interval1, interval2) == expected - # check invalid argument with pytest.raises(TypeError): cmp(interval1, 'invalid type') -@pytest.mark.parametrize('start1,end1,start2,end2,expected', [ - (1, 2, 2, 3, [(1, 3)]), - (1, 3, 2, 4, [(1, 4)]), - (1, 2, 3, 4, [(1, 2), (3, 4)]), - (1, 5, 2, 3, [(1, 5)]), -], ids=( - 'adjacent', - 'overlapping', - 'non-overlapping', - 'contains', -)) +@pytest.mark.parametrize( + 'start1,end1,start2,end2,expected', + [ + (1, 2, 2, 3, [(1, 3)]), + (1, 3, 2, 4, [(1, 4)]), + (1, 2, 3, 4, [(1, 2), (3, 4)]), + (1, 5, 2, 3, [(1, 5)]), + ], + ids=('adjacent', 'overlapping', 'non-overlapping', 'contains'), +) def test_interval_combine(start1, end1, start2, end2, expected): base = maya.now() interval1 = maya.MayaInterval( - start=base.add(days=start1), - end=base.add(days=end1), + start=base.add(days=start1), end=base.add(days=end1) ) interval2 = maya.MayaInterval( - start=base.add(days=start2), - end=base.add(days=end2), + start=base.add(days=start2), end=base.add(days=end2) ) - expected_intervals = [maya.MayaInterval( - start=base.add(days=start), - end=base.add(days=end), - ) for start, end in expected] - + expected_intervals = [ + maya.MayaInterval(start=base.add(days=start), end=base.add(days=end)) + for start, end in expected + ] assert interval1.combine(interval2) == expected_intervals assert interval2.combine(interval1) == expected_intervals - # check invalid argument with pytest.raises(TypeError): interval2.combine('invalid type') -@pytest.mark.parametrize('start1,end1,start2,end2,expected', [ - (1, 2, 3, 4, [(1, 2)]), - (1, 2, 2, 4, [(1, 2)]), - (2, 3, 1, 4, []), - (1, 4, 2, 3, [(1, 2), (3, 4)]), - (1, 4, 0, 2, [(2, 4)]), - (1, 4, 3, 5, [(1, 3)]), - (1, 4, 1, 2, [(2, 4)]), - (1, 4, 3, 4, [(1, 3)]), -], ids=( +@pytest.mark.parametrize( + 'start1,end1,start2,end2,expected', + [ + (1, 2, 3, 4, [(1, 2)]), + (1, 2, 2, 4, [(1, 2)]), + (2, 3, 1, 4, []), + (1, 4, 2, 3, [(1, 2), (3, 4)]), + (1, 4, 0, 2, [(2, 4)]), + (1, 4, 3, 5, [(1, 3)]), + (1, 4, 1, 2, [(2, 4)]), + (1, 4, 3, 4, [(1, 3)]), + ], + ids=( 'non-overlapping', 'adjacent', 'contains', @@ -340,115 +304,109 @@ def test_interval_combine(start1, end1, start2, end2, expected): 'overlaps-right', 'overlaps-left-identical-start', 'overlaps-right-identical-end', -)) + ), +) def test_interval_subtract(start1, end1, start2, end2, expected): base = maya.now() interval1 = maya.MayaInterval( - start=base.add(days=start1), - end=base.add(days=end1), + start=base.add(days=start1), end=base.add(days=end1) ) interval2 = maya.MayaInterval( - start=base.add(days=start2), - end=base.add(days=end2), + start=base.add(days=start2), end=base.add(days=end2) ) - expected_intervals = [maya.MayaInterval( - start=base.add(days=start), - end=base.add(days=end), - ) for start, end in expected] - + expected_intervals = [ + maya.MayaInterval(start=base.add(days=start), end=base.add(days=end)) + for start, end in expected + ] assert interval1.subtract(interval2) == expected_intervals - # check invalid argument with pytest.raises(TypeError): interval1.subtract('invalid type') -@pytest.mark.parametrize('start1,end1,start2,end2,expected', [ - (1, 2, 2, 3, True), - (2, 3, 1, 2, True), - (1, 3, 2, 3, False), - (2, 3, 4, 5, False), -], ids=( - 'adjacent-right', - 'adjacent-left', - 'overlapping', - 'non-overlapping', -)) +@pytest.mark.parametrize( + 'start1,end1,start2,end2,expected', + [ + (1, 2, 2, 3, True), + (2, 3, 1, 2, True), + (1, 3, 2, 3, False), + (2, 3, 4, 5, False), + ], + ids=('adjacent-right', 'adjacent-left', 'overlapping', 'non-overlapping'), +) def test_interval_is_adjacent(start1, end1, start2, end2, expected): base = maya.now() interval1 = maya.MayaInterval( - start=base.add(days=start1), - end=base.add(days=end1), + start=base.add(days=start1), end=base.add(days=end1) ) interval2 = maya.MayaInterval( - start=base.add(days=start2), - end=base.add(days=end2), + start=base.add(days=start2), end=base.add(days=end2) ) assert interval1.is_adjacent(interval2) == expected - # check invalid argument with pytest.raises(TypeError): interval1.is_adjacent('invalid type') -@pytest.mark.parametrize('start,end,delta,include_remainder,expected', [ - (0, 10, 5, False, [(0, 5), (5, 10)]), - (0, 10, 5, True, [(0, 5), (5, 10)]), - (0, 10, 3, False, [(0, 3), (3, 6), (6, 9)]), - (0, 10, 3, True, [(0, 3), (3, 6), (6, 9), (9, 10)]), - (0, 2, 5, False, []), - (0, 2, 5, True, [(0, 2)]), -], ids=( +@pytest.mark.parametrize( + 'start,end,delta,include_remainder,expected', + [ + (0, 10, 5, False, [(0, 5), (5, 10)]), + (0, 10, 5, True, [(0, 5), (5, 10)]), + (0, 10, 3, False, [(0, 3), (3, 6), (6, 9)]), + (0, 10, 3, True, [(0, 3), (3, 6), (6, 9), (9, 10)]), + (0, 2, 5, False, []), + (0, 2, 5, True, [(0, 2)]), + ], + ids=( 'even-split', 'even-split-include-partial', 'uneven-split-do-not-include-partial', 'uneven-split-include-partial', 'delta-larger-than-timepsan-do-not-include-partial', 'delta-larger-than-timepsan-include-partial', -)) + ), +) def test_interval_split(start, end, delta, include_remainder, expected): base = maya.now() interval = maya.MayaInterval( - start=base.add(days=start), - end=base.add(days=end), + start=base.add(days=start), end=base.add(days=end) ) delta = timedelta(days=delta) - expected_intervals = [ - maya.MayaInterval( - start=base.add(days=s), - end=base.add(days=e), - ) for s, e in expected + maya.MayaInterval(start=base.add(days=s), end=base.add(days=e)) + for s, e in expected ] - - assert expected_intervals == list(interval.split( - delta, include_remainder=include_remainder)) + assert expected_intervals == list( + interval.split(delta, include_remainder=include_remainder) + ) def test_interval_split_non_positive_delta(): start = maya.now() end = start.add(days=1) interval = maya.MayaInterval(start=start, end=end) - with pytest.raises(ValueError): list(interval.split(timedelta(seconds=0))) - with pytest.raises(ValueError): list(interval.split(timedelta(seconds=-10))) -@pytest.mark.parametrize('start,end,minutes,timezone,snap_out,expected_start,expected_end', [ - ((5, 12), (8, 48), 30, None, False, (5, 30), (8, 30)), - ((5, 12), (8, 48), 30, None, True, (5, 0), (9, 0)), - ((5, 15), (9, 0), 15, None, False, (5, 15), (9, 0)), - ((5, 15), (9, 0), 15, None, True, (5, 15), (9, 0)), - ((6, 50), (9, 15), 60, 'America/New_York', False, (7, 0), (9, 0)), - ((6, 50), (9, 15), 60, 'America/New_York', True, (6, 0), (10, 0)), - ((6, 20), (6, 50), 60, None, False, (6, 0), (6, 0)), - ((6, 20), (6, 50), 60, None, True, (6, 0), (7, 0)), - ((6, 20), (6, 50), 60, 'America/Chicago', False, (6, 0), (6, 0)), - ((6, 20), (6, 50), 60, 'America/Chicago', True, (6, 0), (7, 0)), -], ids=( +@pytest.mark.parametrize( + 'start,end,minutes,timezone,snap_out,expected_start,expected_end', + [ + ((5, 12), (8, 48), 30, None, False, (5, 30), (8, 30)), + ((5, 12), (8, 48), 30, None, True, (5, 0), (9, 0)), + ((5, 15), (9, 0), 15, None, False, (5, 15), (9, 0)), + ((5, 15), (9, 0), 15, None, True, (5, 15), (9, 0)), + ((6, 50), (9, 15), 60, 'America/New_York', False, (7, 0), (9, 0)), + ((6, 50), (9, 15), 60, 'America/New_York', True, (6, 0), (10, 0)), + ((6, 20), (6, 50), 60, None, False, (6, 0), (6, 0)), + ((6, 20), (6, 50), 60, None, True, (6, 0), (7, 0)), + ((6, 20), (6, 50), 60, 'America/Chicago', False, (6, 0), (6, 0)), + ((6, 20), (6, 50), 60, 'America/Chicago', True, (6, 0), (7, 0)), + ], + ids=( 'normal', 'normal-snap_out', 'already-quantized', @@ -459,21 +417,20 @@ def test_interval_split_non_positive_delta(): 'too-small-snap_out', 'too-small-with-timezone', 'too-small-with-timezone-snap_out', -)) -def test_quantize(start, end, minutes, timezone, snap_out, expected_start, expected_end): + ), +) +def test_quantize( + start, end, minutes, timezone, snap_out, expected_start, expected_end +): base = maya.MayaDT.from_datetime(datetime(2017, 1, 1)) interval = maya.MayaInterval( start=base.add(hours=start[0], minutes=start[1]), end=base.add(hours=end[0], minutes=end[1]), ) - kwargs = {'timezone': timezone} if timezone is not None else {} quantized_interval = interval.quantize( - timedelta(minutes=minutes), - snap_out=snap_out, - **kwargs + timedelta(minutes=minutes), snap_out=snap_out, **kwargs ) - assert quantized_interval == maya.MayaInterval( start=base.add(hours=expected_start[0], minutes=expected_start[1]), end=base.add(hours=expected_end[0], minutes=expected_end[1]), @@ -484,7 +441,6 @@ def test_quantize_invalid_delta(): start = maya.now() end = start.add(days=1) interval = maya.MayaInterval(start=start, end=end) - with pytest.raises(ValueError): interval.quantize(timedelta(minutes=0)) with pytest.raises(ValueError): @@ -495,12 +451,13 @@ def test_interval_flatten_non_overlapping(): step = 2 max_hour = 20 base = maya.now() - intervals = [maya.MayaInterval( - start=base.add(hours=hour), - duration=timedelta(hours=step - 1), - ) for hour in range(0, max_hour, step)] + intervals = [ + maya.MayaInterval( + start=base.add(hours=hour), duration=timedelta(hours=step - 1) + ) + for hour in range(0, max_hour, step) + ] random.shuffle(intervals) - assert maya.MayaInterval.flatten(intervals) == sorted(intervals) @@ -508,35 +465,35 @@ def test_interval_flatten_adjacent(): step = 2 max_hour = 20 base = maya.when('jan/1/2011') - intervals = [ maya.MayaInterval( - start=base.add(hours=hour), - duration=timedelta(hours=step), - ) for hour in range(0, max_hour, step) + start=base.add(hours=hour), duration=timedelta(hours=step) + ) + for hour in range(0, max_hour, step) ] random.shuffle(intervals) - - assert maya.MayaInterval.flatten(intervals) == [maya.MayaInterval( - start=base, - duration=timedelta(hours=max_hour), - )] + assert maya.MayaInterval.flatten(intervals) == [ + maya.MayaInterval(start=base, duration=timedelta(hours=max_hour)) + ] def test_interval_flatten_intersecting(): step = 2 max_hour = 20 base = maya.now() - intervals = [maya.MayaInterval( - start=base.add(hours=hour), - duration=timedelta(hours=step, minutes=30), - ) for hour in range(0, max_hour, step)] + intervals = [ + maya.MayaInterval( + start=base.add(hours=hour), + duration=timedelta(hours=step, minutes=30), + ) + for hour in range(0, max_hour, step) + ] random.shuffle(intervals) - - assert maya.MayaInterval.flatten(intervals) == [maya.MayaInterval( - start=base, - duration=timedelta(hours=max_hour, minutes=30), - )] + assert maya.MayaInterval.flatten(intervals) == [ + maya.MayaInterval( + start=base, duration=timedelta(hours=max_hour, minutes=30) + ) + ] def test_interval_flatten_containing(): @@ -544,16 +501,16 @@ def test_interval_flatten_containing(): max_hour = 20 base = maya.now() containing_interval = maya.MayaInterval( - start=base, - end=base.add(hours=max_hour + step), + start=base, end=base.add(hours=max_hour + step) ) - intervals = [maya.MayaInterval( - start=base.add(hours=hour), - duration=timedelta(hours=step - 1), - ) for hour in range(2, max_hour, step)] + intervals = [ + maya.MayaInterval( + start=base.add(hours=hour), duration=timedelta(hours=step - 1) + ) + for hour in range(2, max_hour, step) + ] intervals.append(containing_interval) random.shuffle(intervals) - assert maya.MayaInterval.flatten(intervals) == [containing_interval] @@ -561,24 +518,18 @@ def test_interval_from_datetime(): start = maya.now() duration = timedelta(hours=1) end = start + duration - interval = maya.MayaInterval.from_datetime( - start_dt=start.datetime(naive=False), - end_dt=end.datetime(naive=False), + start_dt=start.datetime(naive=False), end_dt=end.datetime(naive=False) ) assert interval.start == start assert interval.end == end - interval2 = maya.MayaInterval.from_datetime( - start_dt=start.datetime(naive=False), - duration=duration, + start_dt=start.datetime(naive=False), duration=duration ) assert interval2.start == start assert interval2.end == end - interval3 = maya.MayaInterval.from_datetime( - end_dt=end.datetime(naive=False), - duration=duration, + end_dt=end.datetime(naive=False), duration=duration ) assert interval3.start == start assert interval3.end == end