8000 Avoid IEEE754 converting fraction of seconds · PyMySQL/PyMySQL@4229e39 · GitHub
[go: up one dir, main page]

Skip to content

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 4229e39

Browse files
committed
Avoid IEEE754 converting fraction of seconds
Due to the limitations of IEEE754, we may lost precision when converting string back to datetime objects, for example: `float('0.511581') * 1e6`, gives us `0.511580`, which is unexpected. In order to address that issue, use of `float` is avoided
1 parent 2525d0a commit 4229e39

File tree

2 files changed

+20
-4
lines changed

2 files changed

+20
-4
lines changed

pymysql/converters.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,11 @@ def escape_date(obj, mapping=None):
145145
def escape_struct_time(obj, mapping=None):
146146
return escape_datetime(datetime.datetime(*obj[:6]))
147147

148+
def _convert_second_fraction(s):
149+
# Pad zeros to ensure the fraction length in microseconds
150+
s = s.ljust(6, '0')
151+
return int(s[:6])
152+
148153
def convert_datetime(obj):
149154
"""Returns a DATETIME or TIMESTAMP column value as a datetime object:
150155
@@ -175,7 +180,7 @@ def convert_datetime(obj):
175180
usecs = '0'
176181
if '.' in hms:
177182
hms, usecs = hms.split('.')
178-
usecs = float('0.' + usecs) * 1e6
183+
usecs = _convert_second_fraction(usecs)
179184
return datetime.datetime(*[ int(x) for x in ymd.split('-')+hms.split(':')+[usecs] ])
180185
except ValueError:
181186
return convert_date(obj)
@@ -204,7 +209,7 @@ def convert_timedelta(obj):
204209
microseconds = 0
205210
if "." in obj:
206211
(obj, tail) = obj.split('.')
207-
microseconds = float('0.' + tail) * 1e6
212+
microseconds = _convert_second_fraction(tail)
208213
hours, minutes, seconds = obj.split(':')
209214
negate = 1
210215
if hours.startswith("-"):
@@ -248,7 +253,7 @@ def convert_time(obj):
248253
microseconds = 0
249254
if "." in obj:
250255
(obj, tail) = obj.split('.')
251-
microseconds = float('0.' + tail) * 1e6
256+
microseconds = _convert_second_fraction(tail)
252257
hours, minutes, seconds = obj.split(':')
253258
return datetime.time(hour=int(hours), minute=int(minutes),
254259
second=int(seconds), microsecond=int(microseconds))
@@ -324,7 +329,7 @@ def through(x):
324329
#def convert_bit(b):
325330
# b = "\x00" * (8 - len(b)) + b # pad w/ zeroes
326331
# return struct.unpack(">Q", b)[0]
327-
#
332+
#
328333
# the snippet above is right, but MySQLdb doesn't process bits,
329334
# so we shouldn't either
330335
convert_bit = through

pymysql/tests/test_converters.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import datetime
12
from unittest import TestCase
23

34
from pymysql._compat import PY2
@@ -21,3 +22,13 @@ def test_escape_string_bytes(self):
2122
converters.escape_string(b"foo\nbar"),
2223
b"foo\\nbar"
2324
)
25+
26+
def test_convert_datetime_with_fsp(self):
27+
expected = datetime.datetime(2007, 2, 24, 23, 6, 20, 511581)
28+
dt = converters.convert_datetime('2007-02-24T23:06:20.511581')
29+
self.assertEqual(dt, expected)
30+
31+
def test_convert_time_with_fsp(self):
32+
expected = datetime.time(23, 6, 20, 511581)
33+
time_obj = converters.convert_time('23:06:20.511581')
34+
self.assertEqual(time_obj, expected)

0 commit comments

Comments
 (0)
0