Skip to content

Commit 9a415b7

Browse files
Merge pull request #11 from nickmaccarthy/issue_8
Issue 8
2 parents bbe651a + ba21c07 commit 9a415b7

File tree

11 files changed

+282
-104
lines changed

11 files changed

+282
-104
lines changed

.travis.yml

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
language: python
2-
python:
3-
- "2.6"
4-
- "2.7"
5-
- "3.2"
6-
- "3.3"
7-
- "3.4"
8-
- "3.5"
9-
install: "pip install -r requirements.txt"
10-
script: python tests.py
2+
jobs:
3+
include:
4+
- stage: Python 2.7 build
5+
python:
6+
- "2.7"
7+
install: "pip install -r requirements-2.txt"
8+
script: python tests.py
9+
10+
- stage: Python 3+ builds
11+
python:
12+
- "3.7"
13+
install: "pip install -r requirements-3.txt"
14+
script: python tests.py

CHANGELOG.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Changelog
2+
3+
## 1.4.9 (2019-10-26)
4+
5+
* [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()`.
6+
* [FIX] Arrow upgrade to `0.15.2` to fix the above mentioned issues
7+
* [NEW] Breakout of python2 and python3 requirements
8+
* [NEW] Breakout of python2 and python3 specific CICD pipelines
9+
* [NEW] Derecated the following python version (although they may still work, they are no longer supported) - `2.4`,`2.6`,`3.4`,`3.5`
10+
* [FIX] Modifed `tests.py` to account for the timestamp change (tz is now `+0000`, instead of `-0000`)
11+
* [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)
12+
13+
## v1.4.8 (2019-10-25)
14+
* skipped due to name conflict on pypi, all changes are in `1.4.9`
15+
16+
## v1.4.7 (2017-11-10)
17+
* [FIX] Fixed timezone for date strings: [Issue #6](https://github.com/nickmaccarthy/python-datemath/issues/6)
18+
19+
## v1.4.5 (2017-03-21)
20+
* [NEW] Added roundDown functionality. Allows user to specify the default rounding for expressions such as `/d`.
21+
* example - assuming the time is currently 2016-01-01 12:00:00, we should get the following
22+
```
23+
>>> # now = 2016-01-01 14:00:00+00:00
24+
>>> dm('now+/d', roundDown=False)
25+
<Arrow [2016-01-01T23:59:00+00:00]>
26+
>>> dm('now/d')
27+
<Arrow [2016-01-01T00:00:00+00:00]>
28+
```
29+
30+
## v1.4.4 (2016-12-28)
31+
* [FIX] Fixed bug with expression logic and rounding: https://github.com/nickmaccarthy/python-datemath/pull/2
32+
33+
## 1.4.3 (2016-03-31)
34+
* [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.

README.md

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ datetime.datetime(2016, 1, 22, 22, 58, 28, 338060, tzinfo=tzutc())
114114
datetime.datetime(2016, 1, 24, 22, 57, 45, 394470, tzinfo=tzutc())
115115
```
116116

117-
Or you can just import the `datamath` module, this will always give us a native `datetime` object
117+
Or you can just import the `datemath` module, this will always give us a native `datetime` object
118118
```
119119
>>> from datemath import datemath
120120
>>>
@@ -152,22 +152,8 @@ from datemath import dm
152152
<Arrow [2017-10-20T09:15:20.000000-08:00]>
153153
```
154154

155-
# Release Notes
156-
* v1.4.7 - Fixed timezone for date strings: https://github.com/nickmaccarthy/python-datemath/issues/6
157-
* v1.4.5 - Added roundDown functionality. Allows user to specify the default rounding for expressions such as `/d`.
158-
* example - assuming the time is currently 2016-01-01 12:00:00, we should get the following
159-
```
160-
>>> # now = 2016-01-01 14:00:00+00:00
161-
>>> dm('now+/d', roundDown=False)
162-
<Arrow [2016-01-01T23:59:00+00:00]>
163-
>>> dm('now/d')
164-
<Arrow [2016-01-01T00:00:00+00:00]>
165-
```
166-
* v1.4.4 - Fixed bug with expression logic and rounding: https://github.com/nickmaccarthy/python-datemath/pull/2
167-
* v1.4.3 - 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.
168-
169-
# Test
170-
```python tests.py```
155+
## Changes
156+
See CHANGELOG.md
171157

172-
Happy date math'ing!
158+
# Happy date math'ing!
173159

VERSION.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
1.4.9

build.sh

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,18 @@
33
# builds our packages for pypi
44

55
source env/bin/activate
6-
pip install -r requirements.txt
6+
pip2.7 install -r requirements-2.txt
7+
pip3.7 install -r requirements-3.txt
78

89
echo "Removing any previous build dir..."
910
rm -rf build
1011
echo "Removing any previous dist dir..."
1112
rm -rf dist
1213

13-
echo "Building for python 2.x..."
14-
python setup.py sdist bdist_wheel
15-
echo "Building for python 3.x..."
16-
/usr/local/bin/python3.5 setup.py sdist bdist_wheel
14+
echo "Building for python 2.7..."
15+
python2.7 setup.py sdist bdist_wheel
16+
echo "Building for python 3.7+..."
17+
python3.7 setup.py sdist bdist_wheel
1718

1819
echo "Running twine upload..."
1920
twine upload dist/*

datemath/helpers.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,11 @@
4040

4141
import arrow
4242
import re
43+
import os
4344
from dateutil import tz
45+
import sys
4446

45-
debug = False
47+
debug = True if os.environ.get('DEBUG') == 'true' else False
4648

4749
class DateMathException(BaseException):
4850
pass
@@ -142,7 +144,7 @@ def parseTime(timestamp, timezone='UTC'):
142144
'''
143145
if timestamp and len(timestamp) >= 4:
144146
ts = arrow.get(timestamp)
145-
ts = ts.replace(tzinfo=tz.gettz(timezone))
147+
ts = ts.replace(tzinfo=timezone)
146148
return ts
147149

148150

@@ -165,7 +167,7 @@ def calculate(now, offsetval, unit):
165167
if unit not in ('days','hours','seconds'):
166168
offsetval = int(offsetval)
167169
try:
168-
now = now.replace(**{unit: offsetval})
170+
now = now.shift(**{unit: offsetval})
169171
if debug: print("Calculate called: now: {}, offsetval: {}, offsetval-type: {}, unit: {}".format(now, offsetval, type(offsetval), unit))
170172
return now
171173
except Exception as e:

requirements.txt renamed to requirements-2.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
appdirs==1.4.3
22
args==0.1.0
3-
arrow==0.7.0
3+
arrow==0.15.2
44
clint==0.5.1
55
linecache2==1.0.0
66
packaging==16.8
7-
pkginfo==1.4.1
7+
pkginfo==1.4.2
88
pyparsing==2.2.0
99
python-dateutil==2.6.0
1010
requests==2.13.0
11-
requests-toolbelt==0.7.1
11+
requests-toolbelt>=0.8.0
1212
six==1.10.0
1313
traceback2==1.4.0
1414
twine>=1.8.1

requirements-3.txt

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
appdirs==1.4.3
2+
args==0.1.0
3+
arrow==0.15.2
4+
bleach==3.1.0
5+
certifi==2019.9.11
6+
chardet==3.0.4
7+
clint==0.5.1
8+
docutils==0.15.2
9+
idna==2.7
10+
linecache2==1.0.0
11+
packaging==16.8
12+
pkginfo==1.4.1
13+
Pygments==2.4.2
14+
pyparsing==2.2.0
15+
python-dateutil==2.6.0
16+
readme-renderer==24.0
17+
requests==2.20.0
18+
requests-toolbelt==0.9.1
19+
six==1.10.0
20+
tqdm==4.36.1
21+
traceback2==1.4.0
22+
twine==2.0.0
23+
unittest2==1.1.0
24+
urllib3==1.24.3
25+
webencodings==0.5.1

setup.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616
with open(path.join(here, 'README.md'), encoding='utf-8') as f:
1717
long_description = f.read()
1818

19-
version = '1.4.7'
19+
with open('VERSION.txt', 'r') as f:
20+
version = f.read()
2021

2122
setup(
2223
name='python-datemath',
@@ -57,13 +58,9 @@
5758

5859
# Specify the Python versions you support here. In particular, ensure
5960
# that you indicate whether you support Python 2, Python 3 or both.
60-
'Programming Language :: Python :: 2.6',
6161
'Programming Language :: Python :: 2.7',
62-
'Programming Language :: Python :: 3',
63-
'Programming Language :: Python :: 3.2',
64-
'Programming Language :: Python :: 3.3',
65-
'Programming Language :: Python :: 3.4',
6662
'Programming Language :: Python :: 3.5',
63+
'Programming Language :: Python :: 3.7'
6764
],
6865

6966
# What does your project relate to?

tests-legacy.py

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
import unittest2 as unittest
2+
import arrow
3+
from datetime import datetime as pydatetime
4+
from datemath import dm
5+
from datemath import datemath
6+
from dateutil import tz
7+
8+
iso8601 = 'YYYY-MM-DDTHH:mm:ssZZ'
9+
class TestDM(unittest.TestCase):
10+
11+
def testParse(self):
12+
13+
# Baisc dates
14+
self.assertEqual(dm('2016.01.02').format(iso8601), '2016-01-02T00:00:00-00:00')
15+
self.assertEqual(dm('2016-01-02').format(iso8601), '2016-01-02T00:00:00-00:00')
16+
self.assertEqual(dm('2016-01-02 01:00:00').format(iso8601), '2016-01-02T01:00:00-00:00')
17+
18+
# Rounding Tests
19+
self.assertEqual(dm('2016-01-01||/d').format('YYYY-MM-DDTHH:mm:ssZZ'), '2016-01-01T00:00:00-00:00')
20+
self.assertEqual(dm('2014-11-18||/y').format('YYYY-MM-DDTHH:mm:ssZZ'), '2014-01-01T00:00:00-00:00')
21+
self.assertEqual(dm('2016-01-01 14:00:00||/w').format('YYYY-MM-DDTHH:mm:ssZZ'), '2015-12-28T00:00:00-00:00')
22+
self.assertEqual(dm('2014-11||/M').format('YYYY-MM-DDTHH:mm:ssZZ'), '2014-11-01T00:00:00-00:00')
23+
self.assertEqual(dm('2016-01-02||/M+1h+1m').format(iso8601), '2016-01-01T01:01:00-00:00')
24+
self.assertEqual(dm('2016-01-02||/d+1h').format(iso8601), '2016-01-02T01:00:00-00:00')
25+
self.assertEqual(dm('2016-01-02T14:02:00||/h').format(iso8601), '2016-01-02T14:00:00-00:00')
26+
self.assertEqual(dm('2016-01-02T14:02:00||/H').format(iso8601), '2016-01-02T14:00:00-00:00')
27+
28+
# Rounding Up Tests
29+
self.assertEqual(dm('2016-01-01||/d', roundDown=False).format('YYYY-MM-DDTHH:mm:ssZZ'), '2016-01-01T23:59:59-00:00')
30+
self.assertEqual(dm('2014-11-18||/y', roundDown=False).format('YYYY-MM-DDTHH:mm:ssZZ'), '2014-12-31T23:59:59-00:00')
31+
32+
# Timezone Tests
33+
self.assertEqual(dm('now', tz='US/Pacific').format(iso8601), arrow.utcnow().to('US/Pacific').format(iso8601))
34+
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')))
35+
self.assertEqual(dm('2016-01-01', tz='UTC'), arrow.get('2016-01-01').to('UTC'))
36+
self.assertEqual(dm('2016-01-01', tz='US/Eastern'), pydatetime(2016, 1, 1, tzinfo=tz.gettz('US/Eastern')))
37+
self.assertEqual(datemath('2016-01-01T01:00:00', tz='US/Central'), pydatetime(2016, 1, 1, 1, 0, 0, tzinfo=tz.gettz('US/Central')))
38+
39+
# relitive formats
40+
# addition
41+
self.assertEqual(dm('+1s').format(iso8601), arrow.utcnow().replace(seconds=+1).format(iso8601))
42+
self.assertEqual(dm('+1m').format(iso8601), arrow.utcnow().replace(minutes=+1).format(iso8601))
43+
self.assertEqual(dm('+1h').format(iso8601), arrow.utcnow().replace(hours=+1).format(iso8601))
44+
self.assertEqual(dm('+1d').format(iso8601), arrow.utcnow().replace(days=+1).format(iso8601))
45+
self.assertEqual(dm('+1w').format(iso8601), arrow.utcnow().replace(weeks=+1).format(iso8601))
46+
self.assertEqual(dm('+1M').format(iso8601), arrow.utcnow().replace(months=+1).format(iso8601))
47+
self.assertEqual(dm('+1Y').format(iso8601), arrow.utcnow().replace(years=+1).format(iso8601))
48+
self.assertEqual(dm('+1y').format(iso8601), arrow.utcnow().replace(years=+1).format(iso8601))
49+
# subtraction
50+
self.assertEqual(dm('-1s').format(iso8601), arrow.utcnow().replace(seconds=-1).format(iso8601))
51+
self.assertEqual(dm('-1m').format(iso8601), arrow.utcnow().replace(minutes=-1).format(iso8601))
52+
self.assertEqual(dm('-1h').format(iso8601), arrow.utcnow().replace(hours=-1).format(iso8601))
53+
self.assertEqual(dm('-1d').format(iso8601), arrow.utcnow().replace(days=-1).format(iso8601))
54+
self.assertEqual(dm('-1w').format(iso8601), arrow.utcnow().replace(weeks=-1).format(iso8601))
55+
self.assertEqual(dm('-1M').format(iso8601), arrow.utcnow().replace(months=-1).format(iso8601))
56+
self.assertEqual(dm('-1Y').format(iso8601), arrow.utcnow().replace(years=-1).format(iso8601))
57+
self.assertEqual(dm('-1y').format(iso8601), arrow.utcnow().replace(years=-1).format(iso8601))
58+
# rounding
59+
self.assertEqual(dm('/s').format(iso8601), arrow.utcnow().floor('second').format(iso8601))
60+
self.assertEqual(dm('/m').format(iso8601), arrow.utcnow().floor('minute').format(iso8601))
61+
self.assertEqual(dm('/h').format(iso8601), arrow.utcnow().floor('hour').format(iso8601))
62+
self.assertEqual(dm('/d').format(iso8601), arrow.utcnow().floor('day').format(iso8601))
63+
self.assertEqual(dm('/w').format(iso8601), arrow.utcnow().floor('week').format(iso8601))
64+
self.assertEqual(dm('/M').format(iso8601), arrow.utcnow().floor('month').format(iso8601))
65+
self.assertEqual(dm('/Y').format(iso8601), arrow.utcnow().floor('year').format(iso8601))
66+
self.assertEqual(dm('/y').format(iso8601), arrow.utcnow().floor('year').format(iso8601))
67+
# rounding up
68+
self.assertEqual(dm('/s', roundDown=False).format(iso8601), arrow.utcnow().ceil('second').format(iso8601))
69+
self.assertEqual(dm('/m', roundDown=False).format(iso8601), arrow.utcnow().ceil('minute').format(iso8601))
70+
self.assertEqual(dm('/h', roundDown=False).format(iso8601), arrow.utcnow().ceil('hour').format(iso8601))
71+
self.assertEqual(dm('/d', roundDown=False).format(iso8601), arrow.utcnow().ceil('day').format(iso8601))
72+
self.assertEqual(dm('/w', roundDown=False).format(iso8601), arrow.utcnow().ceil('week').format(iso8601))
73+
self.assertEqual(dm('/M', roundDown=False).format(iso8601), arrow.utcnow().ceil('month').format(iso8601))
74+
self.assertEqual(dm('/Y', roundDown=False).format(iso8601), arrow.utcnow().ceil('year').format(iso8601))
75+
self.assertEqual(dm('/y', roundDown=False).format(iso8601), arrow.utcnow().ceil('year').format(iso8601))
76+
77+
78+
# roundDown option tests
79+
self.assertEqual(dm('2016-01-01T14:00:00||/d').format(iso8601), '2016-01-01T00:00:00-00:00')
80+
self.assertEqual(dm('2016-01-01T14:00:00||/d', roundDown=False).format(iso8601), '2016-01-01T23:59:59-00:00')
81+
82+
# complicated date math
83+
self.assertEqual(dm('now/d-1h').format(iso8601), arrow.utcnow().floor('day').replace(hours=-1).format(iso8601))
84+
self.assertEqual(dm('+1h').format(iso8601), arrow.utcnow().replace(hours=+1).format(iso8601))
85+
self.assertEqual(dm('/M+2d').format(iso8601), arrow.utcnow().floor('month').replace(days=+2).format(iso8601))
86+
self.assertEqual(dm('now/w+2d-2h').format(iso8601), arrow.utcnow().floor('week').replace(days=+2, hours=-2).format(iso8601))
87+
self.assertEqual(dm('now/M+1w-2h+10s').format(iso8601), arrow.utcnow().floor('month').replace(weeks=+1, hours=-2, seconds=+10).format(iso8601))
88+
self.assertEqual(dm('now-1d/d').format(iso8601), arrow.utcnow().replace(days=-1).floor('day').format(iso8601))
89+
self.assertEqual(dm('now+1d/d').format(iso8601), arrow.utcnow().replace(days=1).floor('day').format(iso8601))
90+
self.assertEqual(dm('now-10d/d').format(iso8601), arrow.utcnow().replace(days=-10).floor('day').format(iso8601))
91+
self.assertEqual(dm('now+10d/d').format(iso8601), arrow.utcnow().replace(days=10).floor('day').format(iso8601))
92+
93+
94+
# future
95+
self.assertEqual(dm('+1s').format(iso8601), arrow.utcnow().replace(seconds=+1).format(iso8601))
96+
self.assertEqual(dm('+1s+2m+3h').format(iso8601), arrow.utcnow().replace(seconds=+1, minutes=+2, hours=+3).format(iso8601))
97+
self.assertEqual(dm('+1m').format(iso8601), arrow.utcnow().replace(minutes=+1).format(iso8601))
98+
self.assertEqual(dm('+1m+5h').format(iso8601), arrow.utcnow().replace(minutes=+1, hours=+5).format(iso8601))
99+
self.assertEqual(dm('/d+1m+5h').format(iso8601), arrow.utcnow().floor('day').replace(minutes=+1, hours=+5).format(iso8601))
100+
self.assertEqual(dm('+1h').format(iso8601), arrow.utcnow().replace(hours=+1).format(iso8601))
101+
self.assertEqual(dm('+1w').format(iso8601), arrow.utcnow().replace(weeks=+1).format(iso8601))
102+
self.assertEqual(dm('+1w+12d').format(iso8601), arrow.utcnow().replace(weeks=+1, days=+12).format(iso8601))
103+
self.assertEqual(dm('+2y').format(iso8601), arrow.utcnow().replace(years=+2).format(iso8601))
104+
self.assertEqual(dm('+2y+22d+4h').format(iso8601), arrow.utcnow().replace(years=+2, days=+22, hours=+4).format(iso8601))
105+
106+
# past
107+
self.assertEqual(dm('-3w').format(iso8601), arrow.utcnow().replace(weeks=-3).format(iso8601))
108+
self.assertEqual(dm('-3W').format(iso8601), arrow.utcnow().replace(weeks=-3).format(iso8601))
109+
self.assertEqual(dm('-3w-2d-6h').format(iso8601), arrow.utcnow().replace(weeks=-3, days=-2, hours=-6).format(iso8601))
110+
self.assertEqual(dm('-3w-2d-22h-36s').format(iso8601), arrow.utcnow().replace(weeks=-3, days=-2, hours=-22, seconds=-36).format(iso8601))
111+
self.assertEqual(dm('-6y-3w-2d-22h-36s').format(iso8601), arrow.utcnow().replace(years=-6, weeks=-3, days=-2, hours=-22, seconds=-36).format(iso8601))
112+
113+
114+
import datetime
115+
delta = datetime.timedelta(seconds=1)
116+
# datetime objects
117+
self.assertAlmostEqual(dm('now').datetime, arrow.utcnow().datetime, delta=delta)
118+
self.assertAlmostEqual(dm('now+1d').datetime, arrow.utcnow().replace(days=+1).datetime, delta=delta)
119+
self.assertAlmostEqual(dm('/w').datetime, arrow.utcnow().floor('week').datetime, delta=delta)
120+
121+
122+
# Floats
123+
self.assertEqual(dm('now-2.5h').format(iso8601), arrow.utcnow().replace(hours=-2.5).format(iso8601))
124+
self.assertEqual(dm('now-2.5d').format(iso8601), arrow.utcnow().replace(days=-2.5).format(iso8601))
125+
126+
127+
if __name__ == "__main__":
128+
unittest.main()

0 commit comments

Comments
 (0)