Skip to content

Commit 59d8fda

Browse files
committed
PYTHON-1058 - Support decoding datetime.isoformat format for $date
This change also adds support for the (+|-)HH offset format.
1 parent 740fc3c commit 59d8fda

File tree

2 files changed

+49
-10
lines changed

2 files changed

+49
-10
lines changed

bson/json_util.py

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -145,23 +145,40 @@ def object_hook(dct):
145145
dtm = dct["$date"]
146146
# mongoexport 2.6 and newer
147147
if isinstance(dtm, string_type):
148+
# Parse offset
149+
if dtm[-1] == 'Z':
150+
dt = dtm[:-1]
151+
offset = 'Z'
152+
elif dtm[-3] == ':':
153+
# (+|-)HH:MM
154+
dt = dtm[:-6]
155+
offset = dtm[-6:]
156+
elif dtm[-5] in ('+', '-'):
157+
# (+|-)HHMM
158+
dt = dtm[:-5]
159+
offset = dtm[-5:]
160+
elif dtm[-3] in ('+', '-'):
161+
# (+|-)HH
162+
dt = dtm[:-3]
163+
offset = dtm[-3:]
164+
else:
165+
dt = dtm
166+
offset = ''
167+
148168
aware = datetime.datetime.strptime(
149-
dtm[:23], "%Y-%m-%dT%H:%M:%S.%f").replace(tzinfo=utc)
150-
offset = dtm[23:]
169+
dt, "%Y-%m-%dT%H:%M:%S.%f").replace(tzinfo=utc)
170+
151171
if not offset or offset == 'Z':
152172
# UTC
153173
return aware
154174
else:
155-
if len(offset) == 5:
156-
# Offset from mongoexport is in format (+|-)HHMM
157-
secs = (int(offset[1:3]) * 3600 + int(offset[3:]) * 60)
158-
elif ':' in offset and len(offset) == 6:
159-
# RFC-3339 format (+|-)HH:MM
175+
if len(offset) == 6:
160176
hours, minutes = offset[1:].split(':')
161177
secs = (int(hours) * 3600 + int(minutes) * 60)
162-
else:
163-
# Not RFC-3339 compliant or mongoexport output.
164-
raise ValueError("invalid format for offset")
178+
elif len(offset) == 5:
179+
secs = (int(offset[1:3]) * 3600 + int(offset[3:]) * 60)
180+
elif len(offset) == 3:
181+
secs = int(offset[1:3]) * 3600
165182
if offset[0] == "-":
166183
secs *= -1
167184
return aware - datetime.timedelta(seconds=secs)

test/test_json_util.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,23 +69,45 @@ def test_datetime(self):
6969

7070
jsn = '{"dt": { "$date" : "1970-01-01T00:00:00.000+0000"}}'
7171
self.assertEqual(EPOCH_AWARE, json_util.loads(jsn)["dt"])
72+
jsn = '{"dt": { "$date" : "1970-01-01T00:00:00.000000+0000"}}'
73+
self.assertEqual(EPOCH_AWARE, json_util.loads(jsn)["dt"])
7274
jsn = '{"dt": { "$date" : "1970-01-01T00:00:00.000+00:00"}}'
7375
self.assertEqual(EPOCH_AWARE, json_util.loads(jsn)["dt"])
76+
jsn = '{"dt": { "$date" : "1970-01-01T00:00:00.000000+00:00"}}'
77+
self.assertEqual(EPOCH_AWARE, json_util.loads(jsn)["dt"])
78+
jsn = '{"dt": { "$date" : "1970-01-01T00:00:00.000000+00"}}'
79+
self.assertEqual(EPOCH_AWARE, json_util.loads(jsn)["dt"])
7480
jsn = '{"dt": { "$date" : "1970-01-01T00:00:00.000Z"}}'
7581
self.assertEqual(EPOCH_AWARE, json_util.loads(jsn)["dt"])
82+
jsn = '{"dt": { "$date" : "1970-01-01T00:00:00.000000Z"}}'
83+
self.assertEqual(EPOCH_AWARE, json_util.loads(jsn)["dt"])
7684
# No explicit offset
7785
jsn = '{"dt": { "$date" : "1970-01-01T00:00:00.000"}}'
7886
self.assertEqual(EPOCH_AWARE, json_util.loads(jsn)["dt"])
87+
jsn = '{"dt": { "$date" : "1970-01-01T00:00:00.000000"}}'
88+
self.assertEqual(EPOCH_AWARE, json_util.loads(jsn)["dt"])
7989
# Localtime behind UTC
8090
jsn = '{"dt": { "$date" : "1969-12-31T16:00:00.000-0800"}}'
8191
self.assertEqual(EPOCH_AWARE, json_util.loads(jsn)["dt"])
92+
jsn = '{"dt": { "$date" : "1969-12-31T16:00:00.000000-0800"}}'
93+
self.assertEqual(EPOCH_AWARE, json_util.loads(jsn)["dt"])
8294
jsn = '{"dt": { "$date" : "1969-12-31T16:00:00.000-08:00"}}'
8395
self.assertEqual(EPOCH_AWARE, json_util.loads(jsn)["dt"])
96+
jsn = '{"dt": { "$date" : "1969-12-31T16:00:00.000000-08:00"}}'
97+
self.assertEqual(EPOCH_AWARE, json_util.loads(jsn)["dt"])
98+
jsn = '{"dt": { "$date" : "1969-12-31T16:00:00.000000-08"}}'
99+
self.assertEqual(EPOCH_AWARE, json_util.loads(jsn)["dt"])
84100
# Localtime ahead of UTC
85101
jsn = '{"dt": { "$date" : "1970-01-01T01:00:00.000+0100"}}'
86102
self.assertEqual(EPOCH_AWARE, json_util.loads(jsn)["dt"])
103+
jsn = '{"dt": { "$date" : "1970-01-01T01:00:00.000000+0100"}}'
104+
self.assertEqual(EPOCH_AWARE, json_util.loads(jsn)["dt"])
87105
jsn = '{"dt": { "$date" : "1970-01-01T01:00:00.000+01:00"}}'
88106
self.assertEqual(EPOCH_AWARE, json_util.loads(jsn)["dt"])
107+
jsn = '{"dt": { "$date" : "1970-01-01T01:00:00.000000+01:00"}}'
108+
self.assertEqual(EPOCH_AWARE, json_util.loads(jsn)["dt"])
109+
jsn = '{"dt": { "$date" : "1970-01-01T01:00:00.000000+01"}}'
110+
self.assertEqual(EPOCH_AWARE, json_util.loads(jsn)["dt"])
89111

90112
dtm = datetime.datetime(1, 1, 1, 1, 1, 1, 0, utc)
91113
jsn = '{"dt": {"$date": -62135593139000}}'

0 commit comments

Comments
 (0)