diff options
author | niemeyer <> | 2008-02-28 03:42:33 +0000 |
---|---|---|
committer | niemeyer <> | 2008-02-28 03:42:33 +0000 |
commit | f296b1a67f3b6a58b81221da98c3cebc177ea31e (patch) | |
tree | d28650c5a32cd620c9bc57b32b5eaf0883a77166 | |
parent | d41f89a862e694afc10a6c1499f7d47d31c2d30c (diff) | |
download | dateutil-f296b1a67f3b6a58b81221da98c3cebc177ea31e.tar.gz |
Fixed another precision problem on conversion of decimal seconds to
microseconds, as reported by Erik Brown. Now they're gone for real,
since it's not using floating point arithmetic anymore.
-rw-r--r-- | NEWS | 9 | ||||
-rw-r--r-- | dateutil/parser.py | 23 | ||||
-rw-r--r-- | test.py | 14 |
3 files changed, 38 insertions, 8 deletions
@@ -1,3 +1,12 @@ +Version 1.4 +----------- + +- Fixed another precision problem on conversion of decimal seconds to + microseconds, as reported by Erik Brown. Now they're gone for real, + since it's not using floating point arithmetic anymore. + + + Version 1.3 ----------- diff --git a/dateutil/parser.py b/dateutil/parser.py index 951c09e..b1b7cc2 100644 --- a/dateutil/parser.py +++ b/dateutil/parser.py @@ -361,9 +361,11 @@ class parser(object): # Check if it's a number try: - value = float(l[i]) + value_repr = l[i] + value = float(value_repr) except ValueError: value = None + if value is not None: # Token is a number len_li = len(l[i]) @@ -387,8 +389,7 @@ class parser(object): # 19990101T235959[.59] res.hour = int(s[:2]) res.minute = int(s[2:4]) - value = float(s[4:]) - res.second, res.microsecond = _parsems(value) + res.second, res.microsecond = _parsems(s[4:]) elif len_li == 8: # YYYYMMDD s = l[i-1] @@ -422,13 +423,15 @@ class parser(object): if value%1: res.second = int(60*(value%1)) elif idx == 2: - res.second, res.microsecond = _parsems(value) + res.second, res.microsecond = \ + _parsems(value_repr) i += 1 if i >= len_l or idx == 2: break # 12h00 try: - value = float(l[i]) + value_repr = l[i] + value = float(value_repr) except ValueError: break else: @@ -448,8 +451,7 @@ class parser(object): res.second = int(60*(value%1)) i += 1 if i < len_l and l[i] == ':': - value = float(l[i+1]) - res.second, res.microsecond = _parsems(value) + res.second, res.microsecond = _parsems(l[i+1]) i += 2 elif i < len_l and l[i] in ('-', '/', '.'): sep = l[i] @@ -871,7 +873,12 @@ def _parsetz(tzstr): def _parsems(value): - return int(value), int(value * 1000000) - int(value) * 1000000 + """Parse a I[.F] seconds value into (seconds, microseconds).""" + if "." not in value: + return int(value), 0 + else: + i, f = value.split(".") + return int(i), int(f.ljust(6, "0")[:6]) # vim:ts=4:sw=4:et @@ -3557,6 +3557,20 @@ class ParserTest(unittest.TestCase): self.assertEquals(dt1.microsecond, 10000) self.assertEquals(dt2.microsecond, 10000) + def testMicrosecondPrecisionErrorReturns(self): + # One more precision issue, discovered by Eric Brown. This should + # be the last one, as we're no longer using floating points. + for ms in [100001, 100000, 99999, 99998, + 10001, 10000, 9999, 9998, + 1001, 1000, 999, 998, + 101, 100, 99, 98]: + dt = datetime(2008, 2, 27, 21, 26, 1, ms) + self.assertEquals(parse(dt.isoformat()), dt) + + def testHighPrecisionSeconds(self): + self.assertEquals(parse("20080227T21:26:01.123456789"), + datetime(2008, 2, 27, 21, 26, 1, 123456)) + def testCustomParserInfo(self): # Custom parser info wasn't working, as Michael Elsdörfer discovered. from dateutil.parser import parserinfo, parser |