Import Upstream version 0.6.0
Sandro Tosi
4 years ago
0 | 0 | |
1 | 1 | CHANGES |
2 | 2 | ======= |
3 | ||
4 | 0.6.0 (2017-10-13) | |
5 | ------------------ | |
6 | ||
7 | - support incomplete month date (Fabien Loffredo) | |
8 | - rely on duck typing when doing duration maths | |
9 | - support ':' as separator in fractional time zones (usrenmae) | |
10 | ||
3 | 11 | |
4 | 12 | 0.5.4 (2015-08-06) |
5 | 13 | ------------------ |
0 | 0 | Metadata-Version: 1.1 |
1 | 1 | Name: isodate |
2 | Version: 0.5.4 | |
2 | Version: 0.6.0 | |
3 | 3 | Summary: An ISO 8601 date/time/duration parser and formatter |
4 | Home-page: http://cheeseshop.python.org/pypi/isodate | |
4 | Home-page: https://github.com/gweis/isodate/ | |
5 | 5 | Author: Gerhard Weis |
6 | 6 | Author-email: gerhard.weis@proclos.com |
7 | 7 | License: BSD |
15 | 15 | .. image:: https://coveralls.io/repos/gweis/isodate/badge.svg?branch=master |
16 | 16 | :target: https://coveralls.io/r/gweis/isodate?branch=master |
17 | 17 | :alt: Coveralls |
18 | .. image:: https://pypip.in/version/isodate/badge.svg | |
19 | :target: https://pypi.python.org/pypi/isodate/ | |
18 | .. image:: https://img.shields.io/pypi/v/isodate.svg | |
19 | :target: https://pypi.python.org/pypi/isodate/ | |
20 | 20 | :alt: Latest Version |
21 | .. image:: https://pypip.in/download/isodate/badge.svg | |
22 | :target: https://pypi.python.org/pypi/isodate/ | |
23 | :alt: Downloads | |
24 | .. image:: https://pypip.in/license/isodate/badge.svg | |
25 | :target: https://pypi.python.org/pypi/isodate/ | |
21 | .. image:: https://img.shields.io/pypi/l/isodate.svg | |
22 | :target: https://pypi.python.org/pypi/isodate/ | |
26 | 23 | :alt: License |
27 | 24 | |
28 | 25 | |
120 | 117 | The doc strings and unit tests should provide rather detailed information about |
121 | 118 | the methods and their limitations. |
122 | 119 | |
123 | The source release provides a *setup.py* script and a *buildout.cfg*. Both can | |
124 | be used to run the unit tests included. | |
120 | The source release provides a *setup.py* script, | |
121 | which can be used to run the unit tests included. | |
125 | 122 | |
126 | 123 | Source code is available at `<http://github.com/gweis/isodate>`_. |
127 | 124 | |
128 | 125 | CHANGES |
129 | 126 | ======= |
127 | ||
128 | 0.6.0 (2017-10-13) | |
129 | ------------------ | |
130 | ||
131 | - support incomplete month date (Fabien Loffredo) | |
132 | - rely on duck typing when doing duration maths | |
133 | - support ':' as separator in fractional time zones (usrenmae) | |
134 | ||
130 | 135 | |
131 | 136 | 0.5.4 (2015-08-06) |
132 | 137 | ------------------ |
264 | 269 | Classifier: Programming Language :: Python |
265 | 270 | Classifier: Programming Language :: Python :: 2.6 |
266 | 271 | Classifier: Programming Language :: Python :: 2.7 |
267 | Classifier: Programming Language :: Python :: 3.2 | |
268 | 272 | Classifier: Programming Language :: Python :: 3.3 |
269 | 273 | Classifier: Programming Language :: Python :: 3.4 |
274 | Classifier: Programming Language :: Python :: 3.5 | |
275 | Classifier: Programming Language :: Python :: 3.6 | |
270 | 276 | Classifier: Programming Language :: Python :: Implementation :: PyPy |
271 | 277 | Classifier: Topic :: Internet |
272 | 278 | Classifier: Topic :: Software Development :: Libraries :: Python Modules |
7 | 7 | .. image:: https://coveralls.io/repos/gweis/isodate/badge.svg?branch=master |
8 | 8 | :target: https://coveralls.io/r/gweis/isodate?branch=master |
9 | 9 | :alt: Coveralls |
10 | .. image:: https://pypip.in/version/isodate/badge.svg | |
11 | :target: https://pypi.python.org/pypi/isodate/ | |
10 | .. image:: https://img.shields.io/pypi/v/isodate.svg | |
11 | :target: https://pypi.python.org/pypi/isodate/ | |
12 | 12 | :alt: Latest Version |
13 | .. image:: https://pypip.in/download/isodate/badge.svg | |
14 | :target: https://pypi.python.org/pypi/isodate/ | |
15 | :alt: Downloads | |
16 | .. image:: https://pypip.in/license/isodate/badge.svg | |
17 | :target: https://pypi.python.org/pypi/isodate/ | |
13 | .. image:: https://img.shields.io/pypi/l/isodate.svg | |
14 | :target: https://pypi.python.org/pypi/isodate/ | |
18 | 15 | :alt: License |
19 | 16 | |
20 | 17 | |
112 | 109 | The doc strings and unit tests should provide rather detailed information about |
113 | 110 | the methods and their limitations. |
114 | 111 | |
115 | The source release provides a *setup.py* script and a *buildout.cfg*. Both can | |
116 | be used to run the unit tests included. | |
112 | The source release provides a *setup.py* script, | |
113 | which can be used to run the unit tests included. | |
117 | 114 | |
118 | 115 | Source code is available at `<http://github.com/gweis/isodate>`_. |
0 | [bdist_wheel] | |
1 | universal = 1 | |
2 | ||
0 | 3 | [egg_info] |
1 | 4 | tag_build = |
2 | tag_svn_revision = 0 | |
3 | 5 | tag_date = 0 |
4 | 6 |
25 | 25 | # CONTRACT, STRICT LIABILITY, OR TORT |
26 | 26 | ############################################################################## |
27 | 27 | import os |
28 | import sys | |
29 | ||
30 | setupargs = {} | |
31 | ||
32 | try: | |
33 | from setuptools import setup | |
34 | setupargs['test_suite'] = 'isodate.tests.test_suite' | |
35 | if sys.version[0] == '3': | |
36 | setupargs['use_2to3'] = True | |
37 | except ImportError: | |
38 | from distutils.core import setup | |
39 | if sys.version[0] == '3': | |
40 | from distutils.command.build_py import build_py_2to3 | |
41 | setupargs['cmdclass'] = {'build_py': build_py_2to3} | |
28 | from setuptools import setup | |
42 | 29 | |
43 | 30 | |
44 | 31 | def read(*rnames): |
45 | 32 | return open(os.path.join(os.path.dirname(__file__), *rnames)).read() |
46 | 33 | |
34 | ||
47 | 35 | setup(name='isodate', |
48 | version='0.5.4', | |
36 | version='0.6.0', | |
49 | 37 | packages=['isodate', 'isodate.tests'], |
50 | 38 | package_dir={'': 'src'}, |
51 | 39 | |
52 | 40 | # dependencies: |
53 | # install_requires = [], | |
41 | install_requires=[ | |
42 | 'six' | |
43 | ], | |
54 | 44 | |
55 | 45 | # PyPI metadata |
56 | 46 | author='Gerhard Weis', |
58 | 48 | description='An ISO 8601 date/time/duration parser and formatter', |
59 | 49 | license='BSD', |
60 | 50 | # keywords = '', |
61 | url='http://cheeseshop.python.org/pypi/isodate', | |
51 | url='https://github.com/gweis/isodate/', | |
62 | 52 | |
63 | 53 | long_description=(read('README.rst') + |
64 | 54 | read('CHANGES.txt') + |
72 | 62 | 'Programming Language :: Python', |
73 | 63 | 'Programming Language :: Python :: 2.6', |
74 | 64 | 'Programming Language :: Python :: 2.7', |
75 | 'Programming Language :: Python :: 3.2', | |
76 | 65 | 'Programming Language :: Python :: 3.3', |
77 | 66 | 'Programming Language :: Python :: 3.4', |
67 | 'Programming Language :: Python :: 3.5', | |
68 | 'Programming Language :: Python :: 3.6', | |
78 | 69 | 'Programming Language :: Python :: Implementation :: PyPy', |
79 | 70 | 'Topic :: Internet', |
80 | 71 | ('Topic :: Software Development :' |
81 | 72 | ': Libraries :: Python Modules'), |
82 | 73 | ], |
83 | **setupargs) | |
74 | test_suite='isodate.tests.test_suite') |
42 | 42 | from isodate.isostrf import DATE_BAS_WEEK, DATE_BAS_WEEK_COMPLETE |
43 | 43 | from isodate.isostrf import DATE_CENTURY, DATE_EXT_COMPLETE |
44 | 44 | from isodate.isostrf import DATE_EXT_ORD_COMPLETE, DATE_EXT_WEEK |
45 | from isodate.isostrf import DATE_EXT_WEEK_COMPLETE, DATE_MONTH, DATE_YEAR | |
45 | from isodate.isostrf import DATE_EXT_WEEK_COMPLETE, DATE_YEAR | |
46 | from isodate.isostrf import DATE_BAS_MONTH, DATE_EXT_MONTH | |
46 | 47 | from isodate.isostrf import TIME_BAS_COMPLETE, TIME_BAS_MINUTE |
47 | 48 | from isodate.isostrf import TIME_EXT_COMPLETE, TIME_EXT_MINUTE |
48 | 49 | from isodate.isostrf import TIME_HOUR |
60 | 61 | 'strftime', 'DATE_BAS_COMPLETE', 'DATE_BAS_ORD_COMPLETE', |
61 | 62 | 'DATE_BAS_WEEK', 'DATE_BAS_WEEK_COMPLETE', 'DATE_CENTURY', |
62 | 63 | 'DATE_EXT_COMPLETE', 'DATE_EXT_ORD_COMPLETE', 'DATE_EXT_WEEK', |
63 | 'DATE_EXT_WEEK_COMPLETE', 'DATE_MONTH', 'DATE_YEAR', | |
64 | 'DATE_EXT_WEEK_COMPLETE', 'DATE_YEAR', | |
65 | 'DATE_BAS_MONTH', 'DATE_EXT_MONTH', | |
64 | 66 | 'TIME_BAS_COMPLETE', 'TIME_BAS_MINUTE', 'TIME_EXT_COMPLETE', |
65 | 67 | 'TIME_EXT_MINUTE', 'TIME_HOUR', 'TZ_BAS', 'TZ_EXT', 'TZ_HOUR', |
66 | 68 | 'DT_BAS_COMPLETE', 'DT_EXT_COMPLETE', 'DT_BAS_ORD_COMPLETE', |
29 | 29 | The class Duration allows to define durations in years and months and can be |
30 | 30 | used as limited replacement for timedelta objects. |
31 | 31 | ''' |
32 | from datetime import date, datetime, timedelta | |
32 | from datetime import timedelta | |
33 | 33 | from decimal import Decimal, ROUND_FLOOR |
34 | 34 | |
35 | 35 | |
120 | 120 | if self.years: |
121 | 121 | params.append('%d years' % self.years) |
122 | 122 | if self.months: |
123 | params.append('%d months' % self.months) | |
123 | fmt = "%d months" | |
124 | if self.months <= 1: | |
125 | fmt = "%d month" | |
126 | params.append(fmt % self.months) | |
124 | 127 | params.append(str(self.tdelta)) |
125 | 128 | return ', '.join(params) |
126 | 129 | |
155 | 158 | Durations can be added with Duration, timedelta, date and datetime |
156 | 159 | objects. |
157 | 160 | ''' |
158 | if isinstance(other, timedelta): | |
159 | newduration = Duration(years=self.years, months=self.months) | |
160 | newduration.tdelta = self.tdelta + other | |
161 | return newduration | |
162 | 161 | if isinstance(other, Duration): |
163 | 162 | newduration = Duration(years=self.years + other.years, |
164 | 163 | months=self.months + other.months) |
165 | 164 | newduration.tdelta = self.tdelta + other.tdelta |
166 | 165 | return newduration |
167 | if isinstance(other, (date, datetime)): | |
166 | try: | |
167 | # try anything that looks like a date or datetime | |
168 | # 'other' has attributes year, month, day | |
169 | # and relies on 'timedelta + other' being implemented | |
168 | 170 | if (not(float(self.years).is_integer() and |
169 | 171 | float(self.months).is_integer())): |
170 | 172 | raise ValueError('fractional years or months not supported' |
178 | 180 | else: |
179 | 181 | newday = other.day |
180 | 182 | newdt = other.replace(year=newyear, month=newmonth, day=newday) |
183 | # does a timedelta + date/datetime | |
181 | 184 | return self.tdelta + newdt |
182 | raise TypeError('unsupported operand type(s) for +: %s and %s' % | |
183 | (self.__class__, other.__class__)) | |
184 | ||
185 | def __radd__(self, other): | |
186 | ''' | |
187 | Add durations to timedelta, date and datetime objects. | |
188 | ''' | |
189 | if isinstance(other, timedelta): | |
185 | except AttributeError: | |
186 | # other probably was not a date/datetime compatible object | |
187 | pass | |
188 | try: | |
189 | # try if other is a timedelta | |
190 | # relies on timedelta + timedelta supported | |
190 | 191 | newduration = Duration(years=self.years, months=self.months) |
191 | 192 | newduration.tdelta = self.tdelta + other |
192 | 193 | return newduration |
193 | if isinstance(other, (date, datetime)): | |
194 | if (not(float(self.years).is_integer() and | |
195 | float(self.months).is_integer())): | |
196 | raise ValueError('fractional years or months not supported' | |
197 | ' for date calculations') | |
198 | newmonth = other.month + self.months | |
199 | carry, newmonth = fquotmod(newmonth, 1, 13) | |
200 | newyear = other.year + self.years + carry | |
201 | maxdays = max_days_in_month(newyear, newmonth) | |
202 | if other.day > maxdays: | |
203 | newday = maxdays | |
204 | else: | |
205 | newday = other.day | |
206 | newdt = other.replace(year=newyear, month=newmonth, day=newday) | |
207 | return newdt + self.tdelta | |
208 | raise TypeError('unsupported operand type(s) for +: %s and %s' % | |
209 | (other.__class__, self.__class__)) | |
194 | except AttributeError: | |
195 | # ignore ... other probably was not a timedelta compatible object | |
196 | pass | |
197 | # we have tried everything .... return a NotImplemented | |
198 | return NotImplemented | |
199 | ||
200 | __radd__ = __add__ | |
210 | 201 | |
211 | 202 | def __mul__(self, other): |
212 | 203 | if isinstance(other, int): |
215 | 206 | months=self.months * other) |
216 | 207 | newduration.tdelta = self.tdelta * other |
217 | 208 | return newduration |
218 | raise TypeError('unsupported operand type(s) for +: %s and %s' % | |
219 | (self.__class__, other.__class__)) | |
220 | ||
221 | def __rmul__(self, other): | |
222 | ||
223 | if isinstance(other, int): | |
224 | newduration = Duration( | |
225 | years=self.years * other, | |
226 | months=self.months * other) | |
227 | newduration.tdelta = self.tdelta * other | |
228 | return newduration | |
229 | raise TypeError('unsupported operand type(s) for +: %s and %s' % | |
230 | (other.__class__, self.__class__)) | |
209 | return NotImplemented | |
210 | ||
211 | __rmul__ = __mul__ | |
231 | 212 | |
232 | 213 | def __sub__(self, other): |
233 | 214 | ''' |
239 | 220 | months=self.months - other.months) |
240 | 221 | newduration.tdelta = self.tdelta - other.tdelta |
241 | 222 | return newduration |
242 | if isinstance(other, timedelta): | |
223 | try: | |
224 | # do maths with our timedelta object .... | |
243 | 225 | newduration = Duration(years=self.years, months=self.months) |
244 | 226 | newduration.tdelta = self.tdelta - other |
245 | 227 | return newduration |
246 | raise TypeError('unsupported operand type(s) for -: %s and %s' % | |
247 | (self.__class__, other.__class__)) | |
228 | except TypeError: | |
229 | # looks like timedelta - other is not implemented | |
230 | pass | |
231 | return NotImplemented | |
248 | 232 | |
249 | 233 | def __rsub__(self, other): |
250 | 234 | ''' |
251 | 235 | It is possible to subtract Duration objecs from date, datetime and |
252 | 236 | timedelta objects. |
253 | ''' | |
254 | # print '__rsub__:', self, other | |
255 | if isinstance(other, (date, datetime)): | |
237 | ||
238 | TODO: there is some weird behaviour in date - timedelta ... | |
239 | if timedelta has seconds or microseconds set, then | |
240 | date - timedelta != date + (-timedelta) | |
241 | for now we follow this behaviour to avoid surprises when mixing | |
242 | timedeltas with Durations, but in case this ever changes in | |
243 | the stdlib we can just do: | |
244 | return -self + other | |
245 | instead of all the current code | |
246 | ''' | |
247 | if isinstance(other, timedelta): | |
248 | tmpdur = Duration() | |
249 | tmpdur.tdelta = other | |
250 | return tmpdur - self | |
251 | try: | |
252 | # check if other behaves like a date/datetime object | |
253 | # does it have year, month, day and replace? | |
256 | 254 | if (not(float(self.years).is_integer() and |
257 | 255 | float(self.months).is_integer())): |
258 | 256 | raise ValueError('fractional years or months not supported' |
267 | 265 | newday = other.day |
268 | 266 | newdt = other.replace(year=newyear, month=newmonth, day=newday) |
269 | 267 | return newdt - self.tdelta |
270 | if isinstance(other, timedelta): | |
271 | tmpdur = Duration() | |
272 | tmpdur.tdelta = other | |
273 | return tmpdur - self | |
274 | raise TypeError('unsupported operand type(s) for -: %s and %s' % | |
275 | (other.__class__, self.__class__)) | |
268 | except AttributeError: | |
269 | # other probably was not compatible with data/datetime | |
270 | pass | |
271 | return NotImplemented | |
276 | 272 | |
277 | 273 | def __eq__(self, other): |
278 | 274 | ''' |
279 | 275 | If the years, month part and the timedelta part are both equal, then |
280 | 276 | the two Durations are considered equal. |
281 | 277 | ''' |
282 | if ((isinstance(other, timedelta) and | |
283 | self.years == 0 and self.months == 0)): | |
278 | if isinstance(other, Duration): | |
279 | if (((self.years * 12 + self.months) == | |
280 | (other.years * 12 + other.months) and | |
281 | self.tdelta == other.tdelta)): | |
282 | return True | |
283 | return False | |
284 | # check if other con be compared against timedelta object | |
285 | # will raise an AssertionError when optimisation is off | |
286 | if self.years == 0 and self.months == 0: | |
284 | 287 | return self.tdelta == other |
285 | if not isinstance(other, Duration): | |
286 | return NotImplemented | |
287 | if (((self.years * 12 + self.months) == | |
288 | (other.years * 12 + other.months) and | |
289 | self.tdelta == other.tdelta)): | |
290 | return True | |
291 | 288 | return False |
292 | 289 | |
293 | 290 | def __ne__(self, other): |
295 | 292 | If the years, month part or the timedelta part is not equal, then |
296 | 293 | the two Durations are considered not equal. |
297 | 294 | ''' |
298 | if ((isinstance(other, timedelta) and | |
299 | self.years == 0 and | |
300 | self.months == 0)): | |
295 | if isinstance(other, Duration): | |
296 | if (((self.years * 12 + self.months) != | |
297 | (other.years * 12 + other.months) or | |
298 | self.tdelta != other.tdelta)): | |
299 | return True | |
300 | return False | |
301 | # check if other can be compared against timedelta object | |
302 | # will raise an AssertionError when optimisation is off | |
303 | if self.years == 0 and self.months == 0: | |
301 | 304 | return self.tdelta != other |
302 | if not isinstance(other, Duration): | |
303 | return NotImplemented | |
304 | if (((self.years * 12 + self.months) != | |
305 | (other.years * 12 + other.months) or | |
306 | self.tdelta != other.tdelta)): | |
307 | return True | |
308 | return False | |
305 | return True | |
309 | 306 | |
310 | 307 | def totimedelta(self, start=None, end=None): |
311 | 308 | ''' |
107 | 107 | cache_entry.append(re.compile(r"(?P<sign>[+-]){%d}(?P<year>[0-9]{%d})" |
108 | 108 | r"-(?P<month>[0-9]{2})" |
109 | 109 | % (sign, yeardigits))) |
110 | # YYYMM or +-YYYYYYMM ... basic incomplete month date format | |
111 | cache_entry.append(re.compile(r"(?P<sign>[+-]){%d}(?P<year>[0-9]{%d})" | |
112 | r"(?P<month>[0-9]{2})" | |
113 | % (sign, yeardigits))) | |
110 | 114 | # 6. year dates: |
111 | 115 | # YYYY or +-YYYYYY ... reduced accuracy specific year |
112 | 116 | cache_entry.append(re.compile(r"(?P<sign>[+-]){%d}(?P<year>[0-9]{%d})" |
121 | 125 | return DATE_REGEX_CACHE[(yeardigits, expanded)] |
122 | 126 | |
123 | 127 | |
124 | def parse_date(datestring, yeardigits=4, expanded=False): | |
128 | def parse_date( | |
129 | datestring, | |
130 | yeardigits=4, expanded=False, defaultmonth=1, defaultday=1): | |
125 | 131 | ''' |
126 | 132 | Parse an ISO 8601 date string into a datetime.date object. |
127 | 133 | |
142 | 148 | YYYY-DDD +-YYYYYY-DDD extended ordinal date |
143 | 149 | YYYYWww +-YYYYYYWww basic incomplete week date |
144 | 150 | YYYY-Www +-YYYYYY-Www extended incomplete week date |
151 | YYYMM +-YYYYYYMM basic incomplete month date | |
145 | 152 | YYY-MM +-YYYYYY-MM incomplete month date |
146 | 153 | YYYY +-YYYYYY incomplete year date |
147 | 154 | YY +-YYYY incomplete century date |
166 | 173 | # FIXME: negative dates not possible with python standard types |
167 | 174 | sign = (groups['sign'] == '-' and -1) or 1 |
168 | 175 | if 'century' in groups: |
169 | return date(sign * (int(groups['century']) * 100 + 1), 1, 1) | |
176 | return date( | |
177 | sign * (int(groups['century']) * 100 + 1), | |
178 | defaultmonth, defaultday) | |
170 | 179 | if 'month' not in groups: # weekdate or ordinal date |
171 | 180 | ret = date(sign * int(groups['year']), 1, 1) |
172 | 181 | if 'week' in groups: |
180 | 189 | (((isotuple[1] == 1) and 1) or 0), |
181 | 190 | days=-isotuple[2] + days) |
182 | 191 | elif 'day' in groups: # ordinal date |
183 | return ret + timedelta(days=int(groups['day'])-1) | |
192 | return ret + timedelta(days=int(groups['day']) - 1) | |
184 | 193 | else: # year date |
185 | return ret | |
194 | return ret.replace(month=defaultmonth, day=defaultday) | |
186 | 195 | # year-, month-, or complete date |
187 | 196 | if 'day' not in groups or groups['day'] is None: |
188 | day = 1 | |
197 | day = defaultday | |
189 | 198 | else: |
190 | 199 | day = int(groups['day']) |
191 | 200 | return date(sign * int(groups['year']), |
192 | int(groups['month']) or 1, day) | |
201 | int(groups['month']) or defaultmonth, day) | |
193 | 202 | raise ISO8601Error('Unrecognised ISO 8601 date format: %r' % datestring) |
194 | 203 | |
195 | 204 |
32 | 32 | from datetime import timedelta |
33 | 33 | from decimal import Decimal |
34 | 34 | import re |
35 | ||
36 | from six import string_types | |
35 | 37 | |
36 | 38 | from isodate.duration import Duration |
37 | 39 | from isodate.isoerror import ISO8601Error |
79 | 81 | The alternative format does not support durations with years, months or |
80 | 82 | days set to 0. |
81 | 83 | """ |
82 | if not isinstance(datestring, basestring): | |
84 | if not isinstance(datestring, string_types): | |
83 | 85 | raise TypeError("Expecting a string %r" % datestring) |
84 | 86 | match = ISO8601_PERIOD_REGEX.match(datestring) |
85 | 87 | if not match: |
47 | 47 | DATE_EXT_ORD_COMPLETE = '%Y-%j' |
48 | 48 | DATE_BAS_WEEK = '%YW%W' |
49 | 49 | DATE_EXT_WEEK = '%Y-W%W' |
50 | DATE_MONTH = '%Y-%m' | |
50 | DATE_BAS_MONTH = '%Y%m' | |
51 | DATE_EXT_MONTH = '%Y-%m' | |
51 | 52 | DATE_YEAR = '%Y' |
52 | 53 | DATE_CENTURY = '%C' |
53 | 54 |
124 | 124 | if 'second' in groups: |
125 | 125 | # round to microseconds if fractional seconds are more precise |
126 | 126 | second = Decimal(groups['second']).quantize(Decimal('.000001')) |
127 | microsecond = (second - int(second)) * long(1e6) | |
127 | microsecond = (second - int(second)) * int(1e6) | |
128 | 128 | # int(...) ... no rounding |
129 | 129 | # to_integral() ... rounding |
130 | 130 | return time(int(groups['hour']), int(groups['minute']), |
133 | 133 | if 'minute' in groups: |
134 | 134 | minute = Decimal(groups['minute']) |
135 | 135 | second = (minute - int(minute)) * 60 |
136 | microsecond = (second - int(second)) * long(1e6) | |
136 | microsecond = (second - int(second)) * int(1e6) | |
137 | 137 | return time(int(groups['hour']), int(minute), int(second), |
138 | 138 | int(microsecond.to_integral()), tzinfo) |
139 | 139 | else: |
141 | 141 | hour = Decimal(groups['hour']) |
142 | 142 | minute = (hour - int(hour)) * 60 |
143 | 143 | second = (minute - int(minute)) * 60 |
144 | microsecond = (second - int(second)) * long(1e6) | |
144 | microsecond = (second - int(second)) * int(1e6) | |
145 | 145 | return time(int(hour), int(minute), int(second), |
146 | 146 | int(microsecond.to_integral()), tzinfo) |
147 | 147 | raise ISO8601Error('Unrecognised ISO 8601 time format: %r' % timestring) |
34 | 34 | from isodate.tzinfo import UTC, FixedOffset, ZERO |
35 | 35 | |
36 | 36 | TZ_REGEX = r"(?P<tzname>(Z|(?P<tzsign>[+-])"\ |
37 | r"(?P<tzhour>[0-9]{2})(:(?P<tzmin>[0-9]{2}))?)?)" | |
37 | r"(?P<tzhour>[0-9]{2})(:?(?P<tzmin>[0-9]{2}))?)?)" | |
38 | 38 | |
39 | 39 | TZ_RE = re.compile(TZ_REGEX) |
40 | 40 |
45 | 45 | test_pickle.test_suite(), |
46 | 46 | ]) |
47 | 47 | |
48 | ||
48 | 49 | if __name__ == '__main__': |
49 | 50 | unittest.main(defaultTest='test_suite') |
29 | 29 | import unittest |
30 | 30 | from datetime import date |
31 | 31 | from isodate import parse_date, ISO8601Error, date_isoformat |
32 | from isodate import DATE_CENTURY, DATE_YEAR, DATE_MONTH | |
32 | from isodate import DATE_CENTURY, DATE_YEAR | |
33 | from isodate import DATE_BAS_MONTH, DATE_EXT_MONTH | |
33 | 34 | from isodate import DATE_EXT_COMPLETE, DATE_BAS_COMPLETE |
34 | 35 | from isodate import DATE_BAS_ORD_COMPLETE, DATE_EXT_ORD_COMPLETE |
35 | 36 | from isodate import DATE_BAS_WEEK, DATE_BAS_WEEK_COMPLETE |
41 | 42 | # and 6 digit years. |
42 | 43 | TEST_CASES = {4: [('19', date(1901, 1, 1), DATE_CENTURY), |
43 | 44 | ('1985', date(1985, 1, 1), DATE_YEAR), |
44 | ('1985-04', date(1985, 4, 1), DATE_MONTH), | |
45 | ('1985-04', date(1985, 4, 1), DATE_EXT_MONTH), | |
46 | ('198504', date(1985, 4, 1), DATE_BAS_MONTH), | |
45 | 47 | ('1985-04-12', date(1985, 4, 12), DATE_EXT_COMPLETE), |
46 | 48 | ('19850412', date(1985, 4, 12), DATE_BAS_COMPLETE), |
47 | 49 | ('1985102', date(1985, 4, 12), DATE_BAS_ORD_COMPLETE), |
55 | 57 | ('1-W1-1', None, DATE_BAS_WEEK_COMPLETE)], |
56 | 58 | 6: [('+0019', date(1901, 1, 1), DATE_CENTURY), |
57 | 59 | ('+001985', date(1985, 1, 1), DATE_YEAR), |
58 | ('+001985-04', date(1985, 4, 1), DATE_MONTH), | |
60 | ('+001985-04', date(1985, 4, 1), DATE_EXT_MONTH), | |
59 | 61 | ('+001985-04-12', date(1985, 4, 12), DATE_EXT_COMPLETE), |
60 | 62 | ('+0019850412', date(1985, 4, 12), DATE_BAS_COMPLETE), |
61 | 63 | ('+001985102', date(1985, 4, 12), DATE_BAS_ORD_COMPLETE), |
124 | 126 | def load_tests(loader, tests, pattern): |
125 | 127 | return test_suite() |
126 | 128 | |
129 | ||
127 | 130 | if __name__ == '__main__': |
128 | 131 | unittest.main(defaultTest='test_suite') |
62 | 62 | '+0400'),), |
63 | 63 | DATE_EXT_WEEK_COMPLETE + 'T' + TIME_EXT_MINUTE + TZ_HOUR, |
64 | 64 | '1985-W15-5T10:15+04'), |
65 | ('1985-W15-5T10:15-0430', | |
66 | datetime(1985, 4, 12, 10, 15, tzinfo=FixedOffset(-4, -30, | |
67 | '-0430'),), | |
68 | DATE_EXT_WEEK_COMPLETE + 'T' + TIME_EXT_MINUTE + TZ_BAS, | |
69 | '1985-W15-5T10:15-0430'), | |
70 | ('1985-W15-5T10:15+04:45', | |
71 | datetime(1985, 4, 12, 10, 15, tzinfo=FixedOffset(4, 45, | |
72 | '+04:45'),), | |
73 | DATE_EXT_WEEK_COMPLETE + 'T' + TIME_EXT_MINUTE + TZ_EXT, | |
74 | '1985-W15-5T10:15+04:45'), | |
65 | 75 | ('20110410T101225.123000Z', |
66 | 76 | datetime(2011, 4, 10, 10, 12, 25, 123000, tzinfo=UTC), |
67 | 77 | DATE_BAS_COMPLETE + 'T' + TIME_BAS_COMPLETE + ".%f" + TZ_BAS, |
141 | 151 | def load_tests(loader, tests, pattern): |
142 | 152 | return test_suite() |
143 | 153 | |
154 | ||
144 | 155 | if __name__ == '__main__': |
145 | 156 | unittest.main(defaultTest='test_suite') |
312 | 312 | self.assertEqual('10 years, 10 months, 10 days, 0:00:10', str(dur)) |
313 | 313 | self.assertEqual('isodate.duration.Duration(10, 10, 0,' |
314 | 314 | ' years=10, months=10)', repr(dur)) |
315 | dur = Duration(months=0) | |
316 | self.assertEqual('0:00:00', str(dur)) | |
317 | dur = Duration(months=1) | |
318 | self.assertEqual('1 month, 0:00:00', str(dur)) | |
315 | 319 | |
316 | 320 | def test_hash(self): |
317 | 321 | ''' |
596 | 600 | def load_tests(loader, tests, pattern): |
597 | 601 | return test_suite() |
598 | 602 | |
603 | ||
599 | 604 | if __name__ == '__main__': |
600 | 605 | unittest.main(defaultTest='test_suite') |
0 | 0 | import unittest |
1 | import cPickle as pickle | |
1 | ||
2 | from six.moves import cPickle as pickle | |
3 | ||
2 | 4 | import isodate |
3 | 5 | |
4 | 6 | |
30 | 32 | pikl = pickle.dumps(dur, proto) |
31 | 33 | if dur != pickle.loads(pikl): |
32 | 34 | raise Exception("not equal") |
33 | except Exception, e: | |
35 | except Exception as e: | |
34 | 36 | failed.append("pickle proto %d failed (%s)" % (proto, repr(e))) |
35 | 37 | self.assertEqual(len(failed), 0, "pickle protos failed: %s" % |
36 | 38 | str(failed)) |
39 | ||
40 | def test_pickle_utc(self): | |
41 | ''' | |
42 | isodate.UTC objects remain the same after pickling. | |
43 | ''' | |
44 | self.assertTrue(isodate.UTC is pickle.loads(pickle.dumps(isodate.UTC))) | |
37 | 45 | |
38 | 46 | |
39 | 47 | def test_suite(): |
49 | 57 | def load_tests(loader, tests, pattern): |
50 | 58 | return test_suite() |
51 | 59 | |
60 | ||
52 | 61 | if __name__ == '__main__': |
53 | 62 | unittest.main(defaultTest='test_suite') |
130 | 130 | def load_tests(loader, tests, pattern): |
131 | 131 | return test_suite() |
132 | 132 | |
133 | ||
133 | 134 | if __name__ == '__main__': |
134 | 135 | unittest.main(defaultTest='test_suite') |
82 | 82 | ('15:27:46-05', time(15, 27, 46, |
83 | 83 | tzinfo=FixedOffset(-5, -0, '-05:00')), |
84 | 84 | TIME_EXT_COMPLETE + TZ_HOUR), |
85 | ('15:27:46-05:30', time(15, 27, 46, | |
86 | tzinfo=FixedOffset(-5, -30, '-05:30')), | |
87 | TIME_EXT_COMPLETE + TZ_EXT), | |
88 | ('15:27:46-0545', time(15, 27, 46, | |
89 | tzinfo=FixedOffset(-5, -45, '-0545')), | |
90 | TIME_EXT_COMPLETE + TZ_BAS), | |
85 | 91 | ('1:17:30', None, TIME_EXT_COMPLETE)] |
86 | 92 | |
87 | 93 | |
138 | 144 | def load_tests(loader, tests, pattern): |
139 | 145 | return test_suite() |
140 | 146 | |
147 | ||
141 | 148 | if __name__ == '__main__': |
142 | 149 | unittest.main(defaultTest='test_suite') |
35 | 35 | ''' |
36 | 36 | return ZERO |
37 | 37 | |
38 | def __reduce__(self): | |
39 | ''' | |
40 | When unpickling a Utc object, return the default instance below, UTC. | |
41 | ''' | |
42 | return _Utc, () | |
43 | ||
44 | ||
38 | 45 | UTC = Utc() |
39 | 46 | # the default instance for UTC. |
47 | ||
48 | ||
49 | def _Utc(): | |
50 | ''' | |
51 | Helper function for unpickling a Utc object. | |
52 | ''' | |
53 | return UTC | |
40 | 54 | |
41 | 55 | |
42 | 56 | class FixedOffset(tzinfo): |
137 | 151 | tt = time.localtime(stamp) |
138 | 152 | return tt.tm_isdst > 0 |
139 | 153 | |
154 | ||
155 | # the default instance for local time zone. | |
140 | 156 | LOCAL = LocalTimezone() |
141 | # the default instance for local time zone. |
0 | 0 | Metadata-Version: 1.1 |
1 | 1 | Name: isodate |
2 | Version: 0.5.4 | |
2 | Version: 0.6.0 | |
3 | 3 | Summary: An ISO 8601 date/time/duration parser and formatter |
4 | Home-page: http://cheeseshop.python.org/pypi/isodate | |
4 | Home-page: https://github.com/gweis/isodate/ | |
5 | 5 | Author: Gerhard Weis |
6 | 6 | Author-email: gerhard.weis@proclos.com |
7 | 7 | License: BSD |
15 | 15 | .. image:: https://coveralls.io/repos/gweis/isodate/badge.svg?branch=master |
16 | 16 | :target: https://coveralls.io/r/gweis/isodate?branch=master |
17 | 17 | :alt: Coveralls |
18 | .. image:: https://pypip.in/version/isodate/badge.svg | |
19 | :target: https://pypi.python.org/pypi/isodate/ | |
18 | .. image:: https://img.shields.io/pypi/v/isodate.svg | |
19 | :target: https://pypi.python.org/pypi/isodate/ | |
20 | 20 | :alt: Latest Version |
21 | .. image:: https://pypip.in/download/isodate/badge.svg | |
22 | :target: https://pypi.python.org/pypi/isodate/ | |
23 | :alt: Downloads | |
24 | .. image:: https://pypip.in/license/isodate/badge.svg | |
25 | :target: https://pypi.python.org/pypi/isodate/ | |
21 | .. image:: https://img.shields.io/pypi/l/isodate.svg | |
22 | :target: https://pypi.python.org/pypi/isodate/ | |
26 | 23 | :alt: License |
27 | 24 | |
28 | 25 | |
120 | 117 | The doc strings and unit tests should provide rather detailed information about |
121 | 118 | the methods and their limitations. |
122 | 119 | |
123 | The source release provides a *setup.py* script and a *buildout.cfg*. Both can | |
124 | be used to run the unit tests included. | |
120 | The source release provides a *setup.py* script, | |
121 | which can be used to run the unit tests included. | |
125 | 122 | |
126 | 123 | Source code is available at `<http://github.com/gweis/isodate>`_. |
127 | 124 | |
128 | 125 | CHANGES |
129 | 126 | ======= |
127 | ||
128 | 0.6.0 (2017-10-13) | |
129 | ------------------ | |
130 | ||
131 | - support incomplete month date (Fabien Loffredo) | |
132 | - rely on duck typing when doing duration maths | |
133 | - support ':' as separator in fractional time zones (usrenmae) | |
134 | ||
130 | 135 | |
131 | 136 | 0.5.4 (2015-08-06) |
132 | 137 | ------------------ |
264 | 269 | Classifier: Programming Language :: Python |
265 | 270 | Classifier: Programming Language :: Python :: 2.6 |
266 | 271 | Classifier: Programming Language :: Python :: 2.7 |
267 | Classifier: Programming Language :: Python :: 3.2 | |
268 | 272 | Classifier: Programming Language :: Python :: 3.3 |
269 | 273 | Classifier: Programming Language :: Python :: 3.4 |
274 | Classifier: Programming Language :: Python :: 3.5 | |
275 | Classifier: Programming Language :: Python :: 3.6 | |
270 | 276 | Classifier: Programming Language :: Python :: Implementation :: PyPy |
271 | 277 | Classifier: Topic :: Internet |
272 | 278 | Classifier: Topic :: Software Development :: Libraries :: Python Modules |
1 | 1 | MANIFEST.in |
2 | 2 | README.rst |
3 | 3 | TODO.txt |
4 | setup.cfg | |
4 | 5 | setup.py |
5 | 6 | src/isodate/__init__.py |
6 | 7 | src/isodate/duration.py |
15 | 16 | src/isodate.egg-info/PKG-INFO |
16 | 17 | src/isodate.egg-info/SOURCES.txt |
17 | 18 | src/isodate.egg-info/dependency_links.txt |
19 | src/isodate.egg-info/requires.txt | |
18 | 20 | src/isodate.egg-info/top_level.txt |
19 | 21 | src/isodate/tests/__init__.py |
20 | 22 | src/isodate/tests/test_date.py |
0 | six |