diff --git a/NEWS b/NEWS index b6221e0..eb97d98 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,22 @@ +Version 1.3 +----------- + +- Fixed precision problem on conversion of decimal seconds to + microseconds, as reported by Skip Montanaro. + +- Fixed bug in constructor of parser, and converted parser classes to + new-style classes. Original report and patch by Michael Elsdörfer. + +- Initialize tzid and comps in tz.py, to prevent the code from ever + raising a NameError (even with broken files). Johan Dahlin suggested + the fix after a pyflakes run. + +- Version is now published in dateutil.__version__, as requested + by Darren Dale. + +- All code is compatible with new-style division. + + Version 1.2 ----------- diff --git a/PKG-INFO b/PKG-INFO index b2795fe..144aa63 100644 --- a/PKG-INFO +++ b/PKG-INFO @@ -1,6 +1,6 @@ Metadata-Version: 1.0 Name: python-dateutil -Version: 1.2 +Version: 1.3 Summary: Extensions to the standard python 2.3+ datetime module Home-page: http://labix.org/python-dateutil Author: Gustavo Niemeyer diff --git a/dateutil/__init__.py b/dateutil/__init__.py index 84d7c3e..4a6faaa 100644 --- a/dateutil/__init__.py +++ b/dateutil/__init__.py @@ -1,8 +1,9 @@ """ -Copyright (c) 2003-2005 Gustavo Niemeyer +Copyright (c) 2003-2007 Gustavo Niemeyer This module offers extensions to the standard python 2.3+ datetime module. """ __author__ = "Gustavo Niemeyer " __license__ = "PSF License" +__version__ = "1.3" diff --git a/dateutil/easter.py b/dateutil/easter.py index e55afdb..d794410 100644 --- a/dateutil/easter.py +++ b/dateutil/easter.py @@ -1,5 +1,5 @@ """ -Copyright (c) 2003-2005 Gustavo Niemeyer +Copyright (c) 2003-2007 Gustavo Niemeyer This module offers extensions to the standard python 2.3+ datetime module. @@ -70,23 +70,23 @@ def easter(year, method=EASTER_WESTERN): if method < 3: # Old method i = (19*g+15)%30 - j = (y+y/4+i)%7 + j = (y+y//4+i)%7 if method == 2: # Extra dates to convert Julian to Gregorian date e = 10 if y > 1600: - e = e+y/100-16-(y/100-16)/4 + e = e+y//100-16-(y//100-16)//4 else: # New method - c = y/100 - h = (c-c/4-(8*c+13)/25+19*g+15)%30 - i = h-(h/28)*(1-(h/28)*(29/(h+1))*((21-g)/11)) - j = (y+y/4+i+2-c+c/4)%7 + c = y//100 + h = (c-c//4-(8*c+13)//25+19*g+15)%30 + i = h-(h//28)*(1-(h//28)*(29//(h+1))*((21-g)//11)) + j = (y+y//4+i+2-c+c//4)%7 # p can be from -6 to 56 corresponding to dates 22 March to 23 May # (later dates apply to method 2, although 23 May never actually occurs) p = i-j+e - d = 1+(p+27+(p+6)/40)%31 - m = 3+(p+26)/30 - return datetime.date(y,m,d) + d = 1+(p+27+(p+6)//40)%31 + m = 3+(p+26)//30 + return datetime.date(int(y),int(m),int(d)) diff --git a/dateutil/parser.py b/dateutil/parser.py index 5f25a4d..951c09e 100644 --- a/dateutil/parser.py +++ b/dateutil/parser.py @@ -1,6 +1,6 @@ # -*- coding:iso-8859-1 -*- """ -Copyright (c) 2003-2005 Gustavo Niemeyer +Copyright (c) 2003-2007 Gustavo Niemeyer This module offers extensions to the standard python 2.3+ datetime module. @@ -8,17 +8,24 @@ __author__ = "Gustavo Niemeyer " __license__ = "PSF License" -import os.path +import datetime import string -import sys import time +import sys +import os + +try: + from cStringIO import StringIO +except ImportError: + from StringIO import StringIO -import datetime import relativedelta import tz + __all__ = ["parse", "parserinfo"] + # Some pointers: # # http://www.cl.cam.ac.uk/~mgk25/iso-time.html @@ -28,12 +35,9 @@ # http://search.cpan.org/author/MUIR/Time-modules-2003.0211/lib/Time/ParseDate.pm # http://stein.cshl.org/jade/distrib/docs/java.text.SimpleDateFormat.html -try: - from cStringIO import StringIO -except ImportError: - from StringIO import StringIO -class _timelex: +class _timelex(object): + def __init__(self, instream): if isinstance(instream, basestring): instream = StringIO(instream) @@ -139,6 +143,7 @@ def split(cls, s): return list(cls(s)) split = classmethod(split) + class _resultbase(object): def __init__(self): @@ -156,7 +161,8 @@ def _repr(self, classname): def __repr__(self): return self._repr(self.__class__.__name__) -class parserinfo: + +class parserinfo(object): # m from a.m/p.m, t from ISO T separator JUMP = [" ", ".", ",", ";", "-", "/", "'", @@ -204,7 +210,7 @@ def __init__(self, dayfirst=False, yearfirst=False): self.yearfirst = yearfirst self._year = time.localtime().tm_year - self._century = self._year/100*100 + self._century = self._year//100*100 def _convert(self, lst): dct = {} @@ -281,15 +287,10 @@ def validate(self, res): return True -class parser: +class parser(object): - def __init__(self, info=parserinfo): - if issubclass(info, parserinfo): - self.info = parserinfo() - elif isinstance(info, parserinfo): - self.info = info - else: - raise TypeError, "Unsupported parserinfo type" + def __init__(self, info=None): + self.info = info or parserinfo() def parse(self, timestr, default=None, ignoretz=False, tzinfos=None, @@ -387,9 +388,7 @@ def _parse(self, timestr, dayfirst=None, yearfirst=None, fuzzy=False): res.hour = int(s[:2]) res.minute = int(s[2:4]) value = float(s[4:]) - res.second = int(value) - if value%1: - res.microsecond = int(1000000*(value%1)) + res.second, res.microsecond = _parsems(value) elif len_li == 8: # YYYYMMDD s = l[i-1] @@ -423,9 +422,7 @@ def _parse(self, timestr, dayfirst=None, yearfirst=None, fuzzy=False): if value%1: res.second = int(60*(value%1)) elif idx == 2: - res.second = int(value) - if value%1: - res.microsecond = int(1000000*(value%1)) + res.second, res.microsecond = _parsems(value) i += 1 if i >= len_l or idx == 2: break @@ -452,9 +449,7 @@ def _parse(self, timestr, dayfirst=None, yearfirst=None, fuzzy=False): i += 1 if i < len_l and l[i] == ':': value = float(l[i+1]) - res.second = int(value) - if value%1: - res.microsecond = int(1000000*(value%1)) + res.second, res.microsecond = _parsems(value) i += 2 elif i < len_l and l[i] in ('-', '/', '.'): sep = l[i] @@ -699,7 +694,8 @@ def parse(timestr, parserinfo=None, **kwargs): else: return DEFAULTPARSER.parse(timestr, **kwargs) -class _tzparser: + +class _tzparser(object): class _result(_resultbase): @@ -868,8 +864,14 @@ def parse(self, tzstr): return res + DEFAULTTZPARSER = _tzparser() def _parsetz(tzstr): return DEFAULTTZPARSER.parse(tzstr) + +def _parsems(value): + return int(value), int(value * 1000000) - int(value) * 1000000 + + # vim:ts=4:sw=4:et diff --git a/dateutil/relativedelta.py b/dateutil/relativedelta.py index cdb63b3..562a7d3 100644 --- a/dateutil/relativedelta.py +++ b/dateutil/relativedelta.py @@ -1,5 +1,5 @@ """ -Copyright (c) 2003-2005 Gustavo Niemeyer +Copyright (c) 2003-2007 Gustavo Niemeyer This module offers extensions to the standard python 2.3+ datetime module. @@ -201,27 +201,27 @@ def __init__(self, dt1=None, dt2=None, def _fix(self): if abs(self.microseconds) > 999999: - s = self.microseconds/abs(self.microseconds) + s = self.microseconds//abs(self.microseconds) div, mod = divmod(self.microseconds*s, 1000000) self.microseconds = mod*s self.seconds += div*s if abs(self.seconds) > 59: - s = self.seconds/abs(self.seconds) + s = self.seconds//abs(self.seconds) div, mod = divmod(self.seconds*s, 60) self.seconds = mod*s self.minutes += div*s if abs(self.minutes) > 59: - s = self.minutes/abs(self.minutes) + s = self.minutes//abs(self.minutes) div, mod = divmod(self.minutes*s, 60) self.minutes = mod*s self.hours += div*s if abs(self.hours) > 23: - s = self.hours/abs(self.hours) + s = self.hours//abs(self.hours) div, mod = divmod(self.hours*s, 24) self.hours = mod*s self.days += div*s if abs(self.months) > 11: - s = self.months/abs(self.months) + s = self.months//abs(self.months) div, mod = divmod(self.months*s, 12) self.months = mod*s self.years += div*s @@ -235,7 +235,7 @@ def _fix(self): def _set_months(self, months): self.months = months if abs(self.months) > 11: - s = self.months/abs(self.months) + s = self.months//abs(self.months) div, mod = divmod(self.months*s, 12) self.months = mod*s self.years = div*s diff --git a/dateutil/rrule.py b/dateutil/rrule.py index 2167d52..4c21d2d 100644 --- a/dateutil/rrule.py +++ b/dateutil/rrule.py @@ -1,5 +1,5 @@ """ -Copyright (c) 2003-2005 Gustavo Niemeyer +Copyright (c) 2003-2007 Gustavo Niemeyer This module offers extensions to the standard python 2.3+ datetime module. diff --git a/dateutil/tz.py b/dateutil/tz.py index 5d5a089..61fe2c1 100644 --- a/dateutil/tz.py +++ b/dateutil/tz.py @@ -1,5 +1,5 @@ """ -Copyright (c) 2003-2005 Gustavo Niemeyer +Copyright (c) 2003-2007 Gustavo Niemeyer This module offers extensions to the standard python 2.3+ datetime module. @@ -753,6 +753,8 @@ def _parse_rfc(self, s): else: i += 1 + tzid = None + comps = [] invtz = False comptype = None for line in lines: diff --git a/dateutil/zoneinfo/zoneinfo-2007f.tar.gz b/dateutil/zoneinfo/zoneinfo-2007f.tar.gz deleted file mode 100644 index 5e5025c..0000000 Binary files a/dateutil/zoneinfo/zoneinfo-2007f.tar.gz and /dev/null differ diff --git a/dateutil/zoneinfo/zoneinfo-2007i.tar.gz b/dateutil/zoneinfo/zoneinfo-2007i.tar.gz new file mode 100644 index 0000000..bac2fe3 Binary files /dev/null and b/dateutil/zoneinfo/zoneinfo-2007i.tar.gz differ diff --git a/setup.py b/setup.py index 0f76991..d852379 100755 --- a/setup.py +++ b/setup.py @@ -4,16 +4,23 @@ from os.path import isfile, join import glob import os +import re + if isfile("MANIFEST"): os.unlink("MANIFEST") + # Get PYTHONLIB with no prefix so --prefix installs work. PYTHONLIB = join(get_python_lib(standard_lib=1, prefix=''), 'site-packages') ZONEINFO = join("dateutil", "zoneinfo") +VERSION = re.search('__version__ = "([^"]+)"', + open("dateutil/__init__.py").read()).group(1) + + setup(name="python-dateutil", - version = "1.2", + version = VERSION, description = "Extensions to the standard python 2.3+ datetime module", author = "Gustavo Niemeyer", author_email = "gustavo@niemeyer.net", diff --git a/test.py b/test.py index 8407029..9a3c53c 100755 --- a/test.py +++ b/test.py @@ -1,6 +1,9 @@ #!/usr/bin/python -import unittest +# -*- encoding: utf-8 -*- from cStringIO import StringIO +import unittest +import calendar +import time import base64 import os @@ -21,8 +24,6 @@ from datetime import * -import calendar -import time class RelativeDeltaTest(unittest.TestCase): now = datetime(2003, 9, 17, 20, 54, 47, 282310) @@ -2949,6 +2950,7 @@ def testBadBySetPosMany(self): class ParserTest(unittest.TestCase): + def setUp(self): self.tzinfos = {"BRST": -10800} self.brsttz = tzoffset("BRST", -10800) @@ -3548,6 +3550,24 @@ def testIncreasingISOFormat(self): self.assertEqual(parse(dt.isoformat()), dt) dt += delta + def testMicrosecondsPrecisionError(self): + # Skip found out that sad precision problem. :-( + dt1 = parse("00:11:25.01") + dt2 = parse("00:12:10.01") + self.assertEquals(dt1.microsecond, 10000) + self.assertEquals(dt2.microsecond, 10000) + + def testCustomParserInfo(self): + # Custom parser info wasn't working, as Michael Elsdörfer discovered. + from dateutil.parser import parserinfo, parser + class myparserinfo(parserinfo): + MONTHS = parserinfo.MONTHS[:] + MONTHS[0] = ("Foo", "Foo") + myparser = parser(myparserinfo()) + dt = myparser.parse("01/Foo/2007") + self.assertEquals(dt, datetime(2007, 1, 1)) + + class EasterTest(unittest.TestCase): easterlist = [ # WESTERN ORTHODOX