From 20e408bf7c105e3cb41a612f6beb4778634ed22b Mon Sep 17 00:00:00 2001 From: Nick MacCarthy Date: Fri, 23 Aug 2024 07:59:45 -0400 Subject: [PATCH 01/19] fix: #36 timezone tests --- datemath/helpers.py | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/datemath/helpers.py b/datemath/helpers.py index f953611..62e6b16 100644 --- a/datemath/helpers.py +++ b/datemath/helpers.py @@ -39,19 +39,22 @@ ''' import arrow +from arrow import Arrow +from datetime import datetime import re import os from dateutil import tz import dateutil import sys from pprint import pprint +from typing import Any, Optional debug = True if os.environ.get('DATEMATH_DEBUG') else False class DateMathException(Exception): pass -def unitMap(c): +def unitMap(c: str) -> str: ''' maps our units ( 'd', 'y', 'M', etc ) to shorthands required for arrow ''' @@ -75,14 +78,7 @@ def unitMap(c): else: raise DateMathException("Not a valid timeunit: {0}".format(c)) -def as_datetime(expression, now, tz='UTC'): - ''' - returns our datemath expression as a python datetime object - note: this has been deprecated and the 'type' argument in parse is the current way - ''' - return parse(expression, now, tz) - -def parse(expression, now=None, tz='UTC', type=None, roundDown=True): +def parse(expression: str, now: Any = None, tz: str = 'UTC', type: Any = None, roundDown: bool = True) -> Arrow: ''' the main meat and potatoes of this this whole thing takes our datemath expression and does our date math @@ -99,7 +95,7 @@ def parse(expression, now=None, tz='UTC', type=None, roundDown=True): if debug: print("parse() - Orig Expression: {0}".format(expression)) math = '' - time = '' + time = None if 'UTC' not in tz: if debug: print("parse() - will now convert tz to {0}".format(tz)) @@ -111,7 +107,7 @@ def parse(expression, now=None, tz='UTC', type=None, roundDown=True): return getattr(now, type) else: return now - elif re.match('\d{10,}', str(expression)): + elif re.match(r'\d{10,}', str(expression)): if debug: print('parse() - found an epoch timestamp') if len(str(expression)) == 13: raise DateMathException('Unable to parse epoch timestamps in millis, please convert to the nearest second to continue - i.e. 1451610061 / 1000') @@ -150,7 +146,7 @@ def parse(expression, now=None, tz='UTC', type=None, roundDown=True): else: return rettime -def parseTime(timestamp, timezone='UTC'): +def parseTime(timestamp: str, timezone: str = 'UTC') -> Arrow: ''' parses a datetime string and returns and arrow object ''' @@ -179,7 +175,7 @@ def parseTime(timestamp, timezone='UTC'): if debug: print('parseTime() - Doesnt look like we have a valid timestamp, raise an exception. timestamp={}'.format(timestamp)) raise DateMathException('Valid length timestamp not provide, you gave me a timestamp of "{}", but I need something that has a len() >= 4'.format(timestamp)) -def roundDate(now, unit, tz='UTC', roundDown=True): +def roundDate(now: Any, unit: str, tz: str = 'UTC', roundDown: bool = True) -> Arrow: ''' rounds our date object ''' @@ -190,7 +186,7 @@ def roundDate(now, unit, tz='UTC', roundDown=True): if debug: print("roundDate() Now: {0}".format(now)) return now -def calculate(now, offsetval, unit): +def calculate(now: Arrow, offsetval: float, unit: str) -> Arrow: ''' calculates our dateobject using arrows replace method see unitMap() for more details @@ -204,13 +200,13 @@ def calculate(now, offsetval, unit): except Exception as e: raise DateMathException('Unable to calculate date: now: {0}, offsetvalue: {1}, unit: {2} - reason: {3}'.format(now,offsetval,unit,e)) -def evaluate(expression, now, timeZone='UTC', roundDown=True): +def evaluate(expression: str, now: Arrow, timeZone: str = 'UTC', roundDown: bool = True) -> Arrow: ''' evaluates our datemath style expression ''' if debug: print('evaluate() - Expression: {0}'.format(expression)) if debug: print('evaluate() - Now: {0}'.format(now)) - val = 0 + val = float(0) i = 0 while i < len(expression): char = expression[i] @@ -225,7 +221,7 @@ def evaluate(expression, now, timeZone='UTC', roundDown=True): val = 0 try: - m = re.match('(\d*[.]?\d+)[\w+-/]', expression[i+1:]) + m = re.match(r'(\d*[.]?\d+)[\w+-/]', expression[i+1:]) if m: num = m.group(1) val = val * 10 + float(num) @@ -239,7 +235,7 @@ def evaluate(expression, now, timeZone='UTC', roundDown=True): val = float(val) else: val = float(-val) - elif re.match('[a-zA-Z]+', char): + elif re.match(r'[a-zA-Z]+', char): now = calculate(now, val, unitMap(char)) else: raise DateMathException(''''{}' is not a valid timeunit for expression: '{}' '''.format(char, expression)) From ec7f8c2f1892318264c6c6d3553ef60be400add2 Mon Sep 17 00:00:00 2001 From: Nick MacCarthy Date: Fri, 23 Aug 2024 08:00:03 -0400 Subject: [PATCH 02/19] fix: bump certifi --- requirements-3.txt | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/requirements-3.txt b/requirements-3.txt index 268d6ea..e7804be 100644 --- a/requirements-3.txt +++ b/requirements-3.txt @@ -1,18 +1,22 @@ appdirs==1.4.3 args==0.1.0 -arrow==0.15.2 +arrow==1.2.3 bleach==3.3.0 -certifi==2019.9.11 +certifi==2024.7.4 chardet==3.0.4 clint==0.5.1 docutils==0.15.2 +freezegun==1.2.2 idna==2.7 linecache2==1.0.0 +mypy==1.5.1 +mypy-extensions==1.0.0 packaging==16.8 pkginfo==1.4.2 Pygments==2.7.4 pyparsing==2.2.0 -python-dateutil==2.6.0 +python-dateutil==2.8.2 +pytz==2023.3 readme-renderer==24.0 requests==2.20.0 requests-toolbelt==0.9.1 @@ -20,6 +24,8 @@ six==1.10.0 tqdm==4.36.1 traceback2==1.4.0 twine==2.0.0 +types-python-dateutil==2.8.19.14 +typing_extensions==4.7.1 unittest2==1.1.0 urllib3==1.24.3 webencodings==0.5.1 From 013d0bafe335ebfe057105041c12e9688c3905c8 Mon Sep 17 00:00:00 2001 From: Nick MacCarthy Date: Fri, 23 Aug 2024 08:00:16 -0400 Subject: [PATCH 03/19] fix: match licence --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 8f9a915..86aee70 100644 --- a/setup.py +++ b/setup.py @@ -40,7 +40,7 @@ author_email='nickmaccarthy@gmail.com', # Choose your license - license='MIT', + license='Apache-2.0', # See https://pypi.python.org/pypi?%3Aaction=list_classifiers classifiers=[ @@ -55,7 +55,7 @@ 'Topic :: Software Development :: Build Tools', # Pick your license as you wish (should match "license" above) - 'License :: OSI Approved :: MIT License', + 'Apache-2.0', # Specify the Python versions you support here. In particular, ensure # that you indicate whether you support Python 2, Python 3 or both. From 892488b87b43529f8d9844bf0cdef627a3b9a0a5 Mon Sep 17 00:00:00 2001 From: Nick MacCarthy Date: Fri, 23 Aug 2024 08:00:37 -0400 Subject: [PATCH 04/19] fix: #36 timezone tests --- tests.py | 42 +++++++++++++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/tests.py b/tests.py index 700aa5a..078e1a8 100644 --- a/tests.py +++ b/tests.py @@ -1,20 +1,22 @@ # !/usr/bin/python # coding=utf-8 +import unittest -import unittest2 as unittest import arrow from datetime import datetime as pydatetime from datetime import timedelta from datemath import dm, datemath from datemath.helpers import DateMathException as DateMathException from dateutil import tz +from freezegun import freeze_time +from zoneinfo import ZoneInfo iso8601 = 'YYYY-MM-DDTHH:mm:ssZZ' class TestDM(unittest.TestCase): - def testParse(self): + def testBasic(self): # Make sure our helpers return the correct objects self.assertIsInstance(datemath('now'), pydatetime) self.assertIsInstance(dm('now'), arrow.arrow.Arrow) @@ -24,6 +26,8 @@ def testParse(self): self.assertEqual(dm('2016-01-02').format(iso8601), '2016-01-02T00:00:00+00:00') self.assertEqual(dm('2016-01-02 01:00:00').format(iso8601), '2016-01-02T01:00:00+00:00') + + def testRounding(self): # Rounding Tests self.assertEqual(dm('2016-01-01||/d').format('YYYY-MM-DDTHH:mm:ssZZ'), '2016-01-01T00:00:00+00:00') self.assertEqual(dm('2014-11-18||/y').format('YYYY-MM-DDTHH:mm:ssZZ'), '2014-01-01T00:00:00+00:00') @@ -38,13 +42,25 @@ def testParse(self): self.assertEqual(dm('2016-01-01||/d', roundDown=False).format('YYYY-MM-DDTHH:mm:ssZZ'), '2016-01-01T23:59:59+00:00') self.assertEqual(dm('2014-11-18||/y', roundDown=False).format('YYYY-MM-DDTHH:mm:ssZZ'), '2014-12-31T23:59:59+00:00') + def testTimezone(self): # Timezone Tests - self.assertEqual(dm('now', tz='US/Pacific').format(iso8601), arrow.utcnow().to('US/Pacific').format(iso8601)) - self.assertEqual(dm('2017-09-22 10:20:00', tz='US/Pacific').datetime, pydatetime(2017, 9, 22, 10, 20, 00, tzinfo=tz.gettz('US/Pacific'))) - self.assertEqual(dm('2016-01-01', tz='UTC'), arrow.get('2016-01-01').to('UTC')) - self.assertEqual(dm('2016-01-01', tz='US/Eastern'), pydatetime(2016, 1, 1, tzinfo=tz.gettz('US/Eastern'))) - self.assertEqual(datemath('2016-01-01T01:00:00', tz='US/Central'), pydatetime(2016, 1, 1, 1, 0, 0, tzinfo=tz.gettz('US/Central'))) - self.assertEqual(datemath('2016-01-01T02:00:00', tz='US/Eastern'), pydatetime(2016, 1, 1, 2, tzinfo=tz.gettz('US/Eastern'))) + with freeze_time(datemath('now/d', tz='US/Pacific')): + self.assertEqual(datemath('now/d', tz='US/Pacific'), pydatetime.now(tz=ZoneInfo("US/Pacific"))) + + with freeze_time(pydatetime(2017, 9, 22, 10, 20, 00, tzinfo=tz.gettz('US/Pacific'))): + self.assertEqual(dm('2017-09-22 10:20:00', tz='US/Pacific').datetime, pydatetime.now(tz=ZoneInfo("US/Pacific"))) + + with freeze_time(datemath('2016-01-01T00:00:00', tz='UTC')): + self.assertEqual(dm('2016-01-01', tz='UTC'), arrow.get('2016-01-01').to('UTC')) + + with freeze_time(datemath('2016-01-01', tz='US/Eastern')): + self.assertEqual(dm('2016-01-01', tz='US/Eastern'), pydatetime(2016, 1, 1, tzinfo=tz.gettz('US/Eastern'))) + + with freeze_time(datemath('2016-01-01T01:00:00', tz='US/Central')): + self.assertEqual(datemath('2016-01-01T01:00:00', tz='US/Central'), pydatetime(2016, 1, 1, 1, 0, 0, tzinfo=tz.gettz('US/Central'))) + + with freeze_time(datemath('2016-01-01T02:00:00', tz='US/Eastern')): + self.assertEqual(datemath('2016-01-01T02:00:00', tz='US/Eastern'), pydatetime(2016, 1, 1, 2, tzinfo=tz.gettz('US/Eastern'))) # TZ offset inside of date string self.assertEqual(datemath('2016-01-01T16:20:00.5+12:00'), pydatetime(2016, 1, 1, 16, 20, 0, 500000, tzinfo=tz.tzoffset(None, timedelta(hours=12)))) @@ -58,7 +74,10 @@ def testParse(self): # If a TZ offset is in a datetime string, and there is a tz param used, the TZ offset will take precedence for the returned timeobj self.assertEqual(datemath('2016-01-01T16:20:00.6+12:00||+2d+1h', tz='US/Eastern'), pydatetime(2016, 1, 3, 17, 20, 0, 600000, tzinfo=tz.tzoffset(None, timedelta(hours=12)))) + + def testRelativeFormats(self): # relitive formats + # addition self.assertEqual(dm('+1s').format(iso8601), arrow.utcnow().shift(seconds=+1).format(iso8601)) self.assertEqual(dm('+1m').format(iso8601), arrow.utcnow().shift(minutes=+1).format(iso8601)) @@ -115,6 +134,8 @@ def testParse(self): self.assertEqual(dm('now+10d/d').format(iso8601), arrow.utcnow().shift(days=10).floor('day').format(iso8601)) self.assertEqual(dm('now-29d/d').format(iso8601), arrow.utcnow().shift(days=-29).floor('day').format(iso8601)) + + def testFuture(self): # future self.assertEqual(dm('+1s').format(iso8601), arrow.utcnow().shift(seconds=+1).format(iso8601)) self.assertEqual(dm('+1s+2m+3h').format(iso8601), arrow.utcnow().shift(seconds=+1, minutes=+2, hours=+3).format(iso8601)) @@ -134,7 +155,7 @@ def testParse(self): self.assertEqual(dm('-3w-2d-22h-36s').format(iso8601), arrow.utcnow().shift(weeks=-3, days=-2, hours=-22, seconds=-36).format(iso8601)) self.assertEqual(dm('-6y-3w-2d-22h-36s').format(iso8601), arrow.utcnow().shift(years=-6, weeks=-3, days=-2, hours=-22, seconds=-36).format(iso8601)) - + def testOther(self): import datetime delta = datetime.timedelta(seconds=1) # datetime objects @@ -157,6 +178,8 @@ def testParse(self): except DateMathException as e: self.assertTrue('Unable to parse epoch timestamps in millis' in str(e)) + + def testExceptions(self): # Catch invalid timeunits self.assertRaises(DateMathException, dm, '+1,') self.assertRaises(DateMathException, dm, '+1.') @@ -176,6 +199,7 @@ def testParse(self): if __name__ == "__main__": + print(datemath('now/d', tz='US/Pacific')) unittest.main() From c56f54f7445ad03eecda796228d9513ad4ad5b24 Mon Sep 17 00:00:00 2001 From: Nick MacCarthy Date: Fri, 23 Aug 2024 08:01:17 -0400 Subject: [PATCH 05/19] feat: #31 typehint support --- .github/workflows/tests.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 9d53c08..ff66faa 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -24,3 +24,5 @@ jobs: run: pip3 install -r requirements-3.txt - name: run the tests run: python3 tests.py + - name: verify type hints + run: mypy datemath From 297361fa588f1dd3bc94bb6e6612b684303a8b7d Mon Sep 17 00:00:00 2001 From: Nick MacCarthy Date: Fri, 23 Aug 2024 08:01:30 -0400 Subject: [PATCH 06/19] feat: makefile add --- Makefile | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 Makefile diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..91f7e43 --- /dev/null +++ b/Makefile @@ -0,0 +1,5 @@ + + +tests: + python3 tests.py + From be08adeb4e49a10c5f52e9964aca80f8a720252c Mon Sep 17 00:00:00 2001 From: Nick MacCarthy Date: Fri, 23 Aug 2024 08:01:44 -0400 Subject: [PATCH 07/19] updates for new release --- CHANGELOG.md | 105 +++++++++++++++++++++++++++++++++++---------------- 1 file changed, 73 insertions(+), 32 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 99cc1f3..bb5dae6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,48 +1,87 @@ # Changelog -## 1.5.5 (2021-04-26) -* [FIX] [Issue #28](https://github.com/nickmaccarthy/python-datemath/issues/28) + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] +### fixed +- Fix: Race condition in timezone tests: https://github.com/nickmaccarthy/python-datemath/issues/36 +- Fix: Updated arrow version: https://github.com/nickmaccarthy/python-datemath/issues/32 +- Fix: mypy type hint checking in tests: https://github.com/nickmaccarthy/python-datemath/issues/31 +- Fix: SyntaxWarning: invalid escape sequence in `re.match()`: https://github.com/nickmaccarthy/python-datemath/pull/39 +- Fix: Licence Classifier: https://github.com/nickmaccarthy/python-datemath/pull/34 +- Fix: Bump certifi to latest: https://github.com/nickmaccarthy/python-datemath/pull/38 +### added +- Feat: Typehint support: https://github.com/nickmaccarthy/python-datemath/issues/31 +- Feat: Revamed CHANGELOG.md to keepachangelog.org format + +### todo +- todo: Fix pypi: https://github.com/nickmaccarthy/python-datemath/issues/33 + +## deprecated +- python 2.7 support. Python 3.8+ will only be supported going forward + +## [1.5.5] - 2021-04-26 +### fixed +- fix: [Issue #28](https://github.com/nickmaccarthy/python-datemath/issues/28) * `datemath()` object now returns the expected `datetime` object instead of an `Arrow` object * added tests to catch invalid object types of helpers -## 1.5.4 (2021-04-20) -* skipped due to name conflict on pypi, all changes in this are from `1.5.3` +## [1.5.4] - 2021-04-20 +### Unused +- skipped due to name conflict on pypi, all changes in this are from `1.5.3` -## 1.5.3 (2021-04-16) -* [FIX] [Issue #25](https://github.com/nickmaccarthy/python-datemath/issues/25) - Fixed an issue where if you provided an invalid timestamp, i.e. `datemath('2')` you would not get an DateMathException back. Also bumped dependencies. +## [1.5.3] - 2021-04-16 +### fixed +- FIX: [Issue #25](https://github.com/nickmaccarthy/python-datemath/issues/25) - Fixed an issue where if you provided an invalid timestamp, i.e. `datemath('2')` you would not get an DateMathException back. Also bumped dependencies. -## 1.5.2 (2020-10-01) -* [FIX] [Issue #21](https://github.com/nickmaccarthy/python-datemath/issues/21) - Fixed an issue where if timezone offset was in a datetime string (ISO8601), the timezone of the returned datemath object would be UTC and not the timezone as specified in the datetime string. +## [1.5.2] - 2020-10-01 +### fixed +- FIX: [Issue #21](https://github.com/nickmaccarthy/python-datemath/issues/21) - Fixed an issue where if timezone offset was in a datetime string (ISO8601), the timezone of the returned datemath object would be UTC and not the timezone as specified in the datetime string. ## 1.5.1 (2020-03-25) -* [FIX] [Issue #15](https://github.com/nickmaccarthy/python-datemath/issues/15) - Fixed issue with parser finding invalid timeunits and throwing correct errors -* [NEW] [Issue #16](https://github.com/nickmaccarthy/python-datemath/issues/16) - Added support for parser to accecpt a epoch/unix timestamp but throw an error on epoch milli's since arrow can't support that. +### fixed +- FIX: [Issue #15](https://github.com/nickmaccarthy/python-datemath/issues/15) - Fixed issue with parser finding invalid timeunits and throwing correct errors +### added +- Feat: [Issue #16](https://github.com/nickmaccarthy/python-datemath/issues/16) - Added support for parser to accecpt a epoch/unix timestamp but throw an error on epoch milli's since arrow can't support that. -## 1.5.0 (2019-11-09) +## 1.5.0 - 2019-11-09 -* [FIX] [Issue #12](https://github.com/nickmaccarthy/python-datemath/issues/12) - missing VERSION.txt. Added MANIFEST.in for sdist build -* [FIX] [PR #13](https://github.com/nickmaccarthy/python-datemath/pull/13) - Fix `BaseException` to `Exception` inheritence, thank you for your contribution @yury-primer! +### fixed +- [Issue #12](https://github.com/nickmaccarthy/python-datemath/issues/12) - missing VERSION.txt. Added MANIFEST.in for sdist build +- [PR #13](https://github.com/nickmaccarthy/python-datemath/pull/13) - Fix `BaseException` to `Exception` inheritence, thank you for your contribution @yury-primer! -## 1.4.9 (2019-10-26) +## [1.4.9] - 2019-10-26 ** PLEASE DO NOT USE THIS VERSION, use `1.5.0+` instead. This may not compile on your system due to a missing VERSION.txt which was fixed in `1.5.0+` ** -* [FIX] [Issue #9](https://github.com/nickmaccarthy/python-datemath/issues/9) && [Issue #8](https://github.com/nickmaccarthy/python-datemath/issues/8) - Fixing deprecated arrow `replace()` function with `shift()`. -* [FIX] Arrow upgrade to `0.15.2` to fix the above mentioned issues -* [NEW] Breakout of python2 and python3 requirements -* [NEW] Breakout of python2 and python3 specific CICD pipelines -* [NEW] Derecated the following python version (although they may still work, they are no longer supported) - `2.4`,`2.6`,`3.4`,`3.5` -* [FIX] Modifed `tests.py` to account for the timestamp change (tz is now `+0000`, instead of `-0000`) -* [FIX] replaced `ts = ts.replace(tzinfo=tz.gettz(timezone))` with `ts = ts.replace(tzinfo=timezone)` in `datemath.helpers.parseTime()` to fix [Issue #7](https://github.com/nickmaccarthy/python-datemath/issues/7) - -## v1.4.8 (2019-10-25) + +### fixed +- [FIX] [Issue #9](https://github.com/nickmaccarthy/python-datemath/issues/9) && [Issue #8](https://github.com/nickmaccarthy/python-datemath/issues/8) - Fixing deprecated arrow `replace()` function with `shift()`. +- [FIX] Arrow upgrade to `0.15.2` to fix the above mentioned issues +- [FIX] Modifed `tests.py` to account for the timestamp change (tz is now `+0000`, instead of `-0000`) +- [FIX] replaced `ts = ts.replace(tzinfo=tz.gettz(timezone))` with `ts = ts.replace(tzinfo=timezone)` in `datemath.helpers.parseTime()` to fix [Issue #7](https://github.com/nickmaccarthy/python-datemath/issues/7) +### added + +- [NEW] Breakout of python2 and python3 requirements +- [NEW] Breakout of python2 and python3 specific CICD pipelines +- [NEW] Derecated the following python version (although they may still work, they are no longer supported) - `2.4`,`2.6`,`3.4`,`3.5` + + +## [1.4.8] - 2019-10-25 +** dont use this version ** * skipped due to name conflict on pypi, all changes are in `1.4.9` -## v1.4.7 (2017-11-10) -* [FIX] Fixed timezone for date strings: [Issue #6](https://github.com/nickmaccarthy/python-datemath/issues/6) +## [1.4.7] - 2017-11-10 +### fixed +- [FIX] Fixed timezone for date strings: [Issue #6](https://github.com/nickmaccarthy/python-datemath/issues/6) -## v1.4.5 (2017-03-21) -* [NEW] Added roundDown functionality. Allows user to specify the default rounding for expressions such as `/d`. -* example - assuming the time is currently 2016-01-01 12:00:00, we should get the following +## [1.4.5] - 2017-03-21 +### added +- [NEW] Added roundDown functionality. Allows user to specify the default rounding for expressions such as `/d`. +- example - assuming the time is currently 2016-01-01 12:00:00, we should get the following ``` >>> # now = 2016-01-01 14:00:00+00:00 >>> dm('now+/d', roundDown=False) @@ -51,8 +90,10 @@ ``` -## v1.4.4 (2016-12-28) -* [FIX] Fixed bug with expression logic and rounding: https://github.com/nickmaccarthy/python-datemath/pull/2 +## [1.4.4] - 2016-12-28 +### fixed +- [FIX] Fixed bug with expression logic and rounding: https://github.com/nickmaccarthy/python-datemath/pull/2 -## 1.4.3 (2016-03-31) -* [NEW] Floats are now supported for days, hours, and seconds units. Example ```now-2.5d```, ```now-3.2h```. Any other unit other than days, hours, or seconds that is a float will be converted to an int and floored due to the datetime() module not being able to handle them. \ No newline at end of file +## [1.4.3] - 2016-03-31 +### added +[NEW] Floats are now supported for days, hours, and seconds units. Example ```now-2.5d```, ```now-3.2h```. Any other unit other than days, hours, or seconds that is a float will be converted to an int and floored due to the datetime() module not being able to handle them. \ No newline at end of file From e6b4834c95c3d6e1f0fbe27510f5cefdd5e893d3 Mon Sep 17 00:00:00 2001 From: Nick MacCarthy Date: Fri, 23 Aug 2024 08:02:04 -0400 Subject: [PATCH 08/19] release added --- RELEASE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASE.md b/RELEASE.md index 837fedf..88619e3 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -1,5 +1,5 @@ # How to release -* Create a new tag/release in github. +* Create a new tag/release in github. * Ensure new tag version matches VERSION.txt * Actions should take care of the rest From 90a339303812337950e139793c6f863655a524fc Mon Sep 17 00:00:00 2001 From: Nick MacCarthy Date: Fri, 23 Aug 2024 08:02:26 -0400 Subject: [PATCH 09/19] v3.0.1 --- VERSION.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION.txt b/VERSION.txt index 9075be4..cb2b00e 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -1.5.5 +3.0.1 From 57ad4224179f0f09f47a399b704ea398d5810369 Mon Sep 17 00:00:00 2001 From: Nick MacCarthy Date: Fri, 23 Aug 2024 08:50:57 -0400 Subject: [PATCH 10/19] feat: add tzdata module --- requirements-3.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements-3.txt b/requirements-3.txt index e7804be..a4920a8 100644 --- a/requirements-3.txt +++ b/requirements-3.txt @@ -26,6 +26,7 @@ traceback2==1.4.0 twine==2.0.0 types-python-dateutil==2.8.19.14 typing_extensions==4.7.1 +tzdata==2024.1 unittest2==1.1.0 urllib3==1.24.3 webencodings==0.5.1 From 6387f54d2a67d1a7d43010edd82a05023dd80c91 Mon Sep 17 00:00:00 2001 From: Nick MacCarthy Date: Fri, 23 Aug 2024 09:02:09 -0400 Subject: [PATCH 11/19] test python version --- .github/workflows/tests.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index ff66faa..2fe4f81 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -17,9 +17,11 @@ jobs: - run: pip2 install -r requirements-2.txt - run: python2 tests.py tests-python3: - runs-on: ubuntu-20.04 + runs-on: ubuntu-20.04 # this should probably get updated to a python 3.9+ image steps: - uses: actions/checkout@v2 + - name: print python verions + run: python3 --version - name: install requirements run: pip3 install -r requirements-3.txt - name: run the tests From ecb675d109115f4b083d48c09fdc41b2d6443204 Mon Sep 17 00:00:00 2001 From: Nick MacCarthy Date: Fri, 23 Aug 2024 13:06:40 -0400 Subject: [PATCH 12/19] fix: use pytz instead of zoneinfo --- tests.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests.py b/tests.py index 078e1a8..c0f9900 100644 --- a/tests.py +++ b/tests.py @@ -10,7 +10,7 @@ from datemath.helpers import DateMathException as DateMathException from dateutil import tz from freezegun import freeze_time -from zoneinfo import ZoneInfo +import pytz iso8601 = 'YYYY-MM-DDTHH:mm:ssZZ' class TestDM(unittest.TestCase): @@ -45,10 +45,10 @@ def testRounding(self): def testTimezone(self): # Timezone Tests with freeze_time(datemath('now/d', tz='US/Pacific')): - self.assertEqual(datemath('now/d', tz='US/Pacific'), pydatetime.now(tz=ZoneInfo("US/Pacific"))) + self.assertEqual(datemath('now/d', tz='US/Pacific'), pydatetime.now(tz=pytz.timezone("US/Pacific"))) with freeze_time(pydatetime(2017, 9, 22, 10, 20, 00, tzinfo=tz.gettz('US/Pacific'))): - self.assertEqual(dm('2017-09-22 10:20:00', tz='US/Pacific').datetime, pydatetime.now(tz=ZoneInfo("US/Pacific"))) + self.assertEqual(dm('2017-09-22 10:20:00', tz='US/Pacific').datetime, pydatetime.now(tz=pytz.timezone("US/Pacific"))) with freeze_time(datemath('2016-01-01T00:00:00', tz='UTC')): self.assertEqual(dm('2016-01-01', tz='UTC'), arrow.get('2016-01-01').to('UTC')) From 7572d6c912613424447f6f34ba250f2d82c9e4db Mon Sep 17 00:00:00 2001 From: Nick MacCarthy Date: Fri, 23 Aug 2024 13:09:01 -0400 Subject: [PATCH 13/19] feat: remove legacy python2 tests --- .github/workflows/tests.yaml | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 2fe4f81..3653459 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -7,20 +7,11 @@ on: branches: - master jobs: - tests-python2: - runs-on: ubuntu-20.04 - steps: - - uses: actions/checkout@v2 - - run: sudo apt install python2 - - run: curl https://bootstrap.pypa.io/pip/2.7/get-pip.py --output get-pip.py - - run: sudo python2 get-pip.py - - run: pip2 install -r requirements-2.txt - - run: python2 tests.py tests-python3: runs-on: ubuntu-20.04 # this should probably get updated to a python 3.9+ image steps: - uses: actions/checkout@v2 - - name: print python verions + - name: print python version run: python3 --version - name: install requirements run: pip3 install -r requirements-3.txt From 331117e6cd61d6938d5fb50ae080c43ecb0aa4e8 Mon Sep 17 00:00:00 2001 From: Nick MacCarthy Date: Fri, 23 Aug 2024 13:34:59 -0400 Subject: [PATCH 14/19] README update --- README.md | 70 +++++++++++++++++++++++++++---------------------------- 1 file changed, 34 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index 7d0f9cd..78b4cd9 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,12 @@ -[![Build Status](https://travis-ci.org/nickmaccarthy/python-datemath.svg?branch=master)](https://travis-ci.org/nickmaccarthy/python-datemath.svg?branch=master) - # Python Datemath ## What? -A date math (aka datemath) parser compatiable with the elasticsearch 'date math' format - -## Why? - -Working with date objects in python has always been interesting. Having a background in php, I have been looking for quite some time ( no pun intended ) for a way to do date time interpolation similar to php's ```strtotime()``` function. While the arrow module comes close, I needed something that could turn date math type strings into datetime objects for use in [tattle.io](http://tattle.io) and other projects I use in elasticsearch. I have found even more uses for it, including AWS cloudwatch and various other projects and hopefully you will too. +A date math (aka datemath) parser compatiable with the elasticsearch "date math" format -## What is date math? +## What is "date math"? -Date Math is the short hand arithmetic to find relative time to fixed moments in date and time. Similar to the SOLR date math format, Elasticsearch has its own built in format for short hand date math and this module aims to support that same coverage in python. - -Documentation from elasticsearch: -[http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/mapping-date-format.html#date-math](http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/mapping-date-format.html#date-math) +Date Math is the short hand arithmetic to find relative time to fixed moments in date and time. Similar to the SOLR date math format, we aim to support that same coverage in python. > The date type supports using date math expression when using it in a query/filter (mainly makes sense in range query/filter). > @@ -31,6 +22,7 @@ Documentation from elasticsearch: ## Unit Maps +The "unit maps" here define the shorthand sytax for the dates/timeframes we are working with: ```yaml y or Y = 'year' M = 'month' @@ -39,17 +31,14 @@ d or D = 'day' w = 'week' h or H = 'hour' s or S = 'second' -``` - -## Install - -```python -pip install python-datemath +now = ``` ## Examples -Assuming our datetime is currently: `2016-01-01T00:00:00-00:00` +Here are some examples of using date math to find dates both in the past and in the future + +Assuming our "now" datetime is currently: `2016-01-01T00:00:00-00:00` ```yaml Expression: Result: @@ -73,7 +62,7 @@ now/Y 2016-12-31T23:59:59+00:00 ## Usage -By default datemath return an arrow date object representing your timestamp. +If you use the `dm` function in the datemath module, we will return an arrow date object representing your timestamp. ```python >>> from datemath import dm @@ -118,8 +107,25 @@ If you would rather have a string, you can use arrow's ```.format()``` method. u'2015.12.18' ``` -Rather have a python datetime object instead? Just pass along the 'datetime' type +If you would rather have your time object come back in standard python `datetime`, use the `datemath` function instead: + +```python +>>> from datemath import datemath +>>> ## Assuming "now" is 2016-01-01T00:00:00 +>>> datemath("now-1h") +datetime.datetime(2015, 12, 31, 23, 0, tzinfo=tzutc()) +# Cast it as a str() get a string of the timestamp back too +>>> str(datemath("now-1h")) +'2015-12-31 23:00:00+00:00' +>>> # roundDown=True is default and implied +>>> datemath('2016-01-01T16:20:00||/d') +datetime.datetime(2016, 1, 1, 0, 0, tzinfo=tzutc()) +>>> # Using the roundDown option +>>> datemath('2016-01-01T16:20:00||/d', roundDown=False) +datetime.datetime(2016, 1, 1, 23, 59, 59, 999999, tzinfo=tzutc()) +``` +Or you can use the `dm` function and set its `type` to `datetime`: ```python from datemath import dm >>> dm('now', type='datetime') @@ -129,21 +135,7 @@ datetime.datetime(2016, 1, 22, 22, 58, 28, 338060, tzinfo=tzutc()) datetime.datetime(2016, 1, 24, 22, 57, 45, 394470, tzinfo=tzutc()) ``` -Or you can just import the `datemath` module, this will always give us a native `datetime` object - -```python ->>> from datemath import datemath ->>> ->>> datemath('2016-01-01T16:20:00||/d', roundDown=False) -datetime.datetime(2016, 1, 1, 23, 59, 59, 999999, tzinfo=tzutc()) ->>> ->>> ->>> # roundDown=True is default and implied ->>> datemath('2016-01-01T16:20:00||/d') -datetime.datetime(2016, 1, 1, 0, 0, tzinfo=tzutc()) -``` - -If you want a Epoch timestamp back instead, we can do that. +If you want a Epoch timestamp back instead, we can do that too. ```python >>> dm('now+2d-1m', type='timestamp') @@ -188,6 +180,12 @@ Note - currently timestrings with a timezone offset and the usage of the ```tz`` ``` +## Install + +```python +pip install python-datemath +``` + ## Debugging If you would like more verbose output to debug the process of what datemath is doing, simply set `export DATEMATH_DEBUG=true` in your shell then run some datemath tests. To stop debugging, run `unset DATEMATH_DEBUG`. From 79bbf61e0aa953958f54a384584a3f3f7f99c257 Mon Sep 17 00:00:00 2001 From: Nick MacCarthy Date: Fri, 23 Aug 2024 13:58:40 -0400 Subject: [PATCH 15/19] README update --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 78b4cd9..2545e33 100644 --- a/README.md +++ b/README.md @@ -144,7 +144,7 @@ If you want a Epoch timestamp back instead, we can do that too. ## What timezone are my objects in? -By default all object returned by datemath are in UTC. +By default all objects returned by datemath are in UTC. If you want them them back in a different timezone, just pass along the ```tz``` argument. Timezone list can be found here: [https://gist.github.com/pamelafox/986163](https://gist.github.com/pamelafox/986163) From 30bef82fc0d909b0bb588bd875a0f70f2c3bdb42 Mon Sep 17 00:00:00 2001 From: Nick MacCarthy Date: Fri, 23 Aug 2024 14:04:41 -0400 Subject: [PATCH 16/19] feat: verify package install --- .github/workflows/tests.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 3653459..3327519 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -19,3 +19,5 @@ jobs: run: python3 tests.py - name: verify type hints run: mypy datemath + - name: verify package install + run: python3 setup.py install From 2c43a7c345c7be2ee1d15eaf866a6079b9cf1859 Mon Sep 17 00:00:00 2001 From: Nick MacCarthy Date: Fri, 23 Aug 2024 14:05:15 -0400 Subject: [PATCH 17/19] support for only python 3.8+ --- setup.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/setup.py b/setup.py index 86aee70..38af6db 100644 --- a/setup.py +++ b/setup.py @@ -59,10 +59,10 @@ # Specify the Python versions you support here. In particular, ensure # that you indicate whether you support Python 2, Python 3 or both. - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3.5', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7' + 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', + 'Programming Language :: Python :: 3.11', ], # What does your project relate to? From 81160292382c3b5fea41edca6a2f98ecdac8ab43 Mon Sep 17 00:00:00 2001 From: Nick MacCarthy Date: Fri, 23 Aug 2024 14:07:53 -0400 Subject: [PATCH 18/19] feat: verify package install --- .github/workflows/tests.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 3327519..d7501a2 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -20,4 +20,4 @@ jobs: - name: verify type hints run: mypy datemath - name: verify package install - run: python3 setup.py install + run: python3 setup.py install --user From 42cd7379f0d5dcaa2bbd12a438104e704b792e79 Mon Sep 17 00:00:00 2001 From: Nick MacCarthy Date: Fri, 23 Aug 2024 14:09:57 -0400 Subject: [PATCH 19/19] feat: add import verification --- .github/workflows/tests.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index d7501a2..218bdb5 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -21,3 +21,5 @@ jobs: run: mypy datemath - name: verify package install run: python3 setup.py install --user + - name: verify we can import + run: python3 -c "from datemath import datemath; print(datemath('now-1d'))"