summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorniemeyer <>2008-02-28 03:42:33 +0000
committerniemeyer <>2008-02-28 03:42:33 +0000
commitf296b1a67f3b6a58b81221da98c3cebc177ea31e (patch)
treed28650c5a32cd620c9bc57b32b5eaf0883a77166
parentd41f89a862e694afc10a6c1499f7d47d31c2d30c (diff)
downloaddateutil-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--NEWS9
-rw-r--r--dateutil/parser.py23
-rw-r--r--test.py14
3 files changed, 38 insertions, 8 deletions
diff --git a/NEWS b/NEWS
index eb97d98..6a80ca4 100644
--- a/NEWS
+++ b/NEWS
@@ -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
diff --git a/test.py b/test.py
index 9a3c53c..c99efff 100644
--- a/test.py
+++ b/test.py
@@ -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