diff --git a/bson/json_util.py b/bson/json_util.py index cbbff7c1b..c8a1ed4a7 100644 --- a/bson/json_util.py +++ b/bson/json_util.py @@ -145,23 +145,40 @@ def object_hook(dct): dtm = dct["$date"] # mongoexport 2.6 and newer if isinstance(dtm, string_type): + # Parse offset + if dtm[-1] == 'Z': + dt = dtm[:-1] + offset = 'Z' + elif dtm[-3] == ':': + # (+|-)HH:MM + dt = dtm[:-6] + offset = dtm[-6:] + elif dtm[-5] in ('+', '-'): + # (+|-)HHMM + dt = dtm[:-5] + offset = dtm[-5:] + elif dtm[-3] in ('+', '-'): + # (+|-)HH + dt = dtm[:-3] + offset = dtm[-3:] + else: + dt = dtm + offset = '' + aware = datetime.datetime.strptime( - dtm[:23], "%Y-%m-%dT%H:%M:%S.%f").replace(tzinfo=utc) - offset = dtm[23:] + dt, "%Y-%m-%dT%H:%M:%S.%f").replace(tzinfo=utc) + if not offset or offset == 'Z': # UTC return aware else: - if len(offset) == 5: - # Offset from mongoexport is in format (+|-)HHMM - secs = (int(offset[1:3]) * 3600 + int(offset[3:]) * 60) - elif ':' in offset and len(offset) == 6: - # RFC-3339 format (+|-)HH:MM + if len(offset) == 6: hours, minutes = offset[1:].split(':') secs = (int(hours) * 3600 + int(minutes) * 60) - else: - # Not RFC-3339 compliant or mongoexport output. - raise ValueError("invalid format for offset") + elif len(offset) == 5: + secs = (int(offset[1:3]) * 3600 + int(offset[3:]) * 60) + elif len(offset) == 3: + secs = int(offset[1:3]) * 3600 if offset[0] == "-": secs *= -1 return aware - datetime.timedelta(seconds=secs) diff --git a/test/test_json_util.py b/test/test_json_util.py index a08f82645..fc12b5a0f 100644 --- a/test/test_json_util.py +++ b/test/test_json_util.py @@ -69,23 +69,45 @@ class TestJsonUtil(unittest.TestCase): jsn = '{"dt": { "$date" : "1970-01-01T00:00:00.000+0000"}}' self.assertEqual(EPOCH_AWARE, json_util.loads(jsn)["dt"]) + jsn = '{"dt": { "$date" : "1970-01-01T00:00:00.000000+0000"}}' + self.assertEqual(EPOCH_AWARE, json_util.loads(jsn)["dt"]) jsn = '{"dt": { "$date" : "1970-01-01T00:00:00.000+00:00"}}' self.assertEqual(EPOCH_AWARE, json_util.loads(jsn)["dt"]) + jsn = '{"dt": { "$date" : "1970-01-01T00:00:00.000000+00:00"}}' + self.assertEqual(EPOCH_AWARE, json_util.loads(jsn)["dt"]) + jsn = '{"dt": { "$date" : "1970-01-01T00:00:00.000000+00"}}' + self.assertEqual(EPOCH_AWARE, json_util.loads(jsn)["dt"]) jsn = '{"dt": { "$date" : "1970-01-01T00:00:00.000Z"}}' self.assertEqual(EPOCH_AWARE, json_util.loads(jsn)["dt"]) + jsn = '{"dt": { "$date" : "1970-01-01T00:00:00.000000Z"}}' + self.assertEqual(EPOCH_AWARE, json_util.loads(jsn)["dt"]) # No explicit offset jsn = '{"dt": { "$date" : "1970-01-01T00:00:00.000"}}' self.assertEqual(EPOCH_AWARE, json_util.loads(jsn)["dt"]) + jsn = '{"dt": { "$date" : "1970-01-01T00:00:00.000000"}}' + self.assertEqual(EPOCH_AWARE, json_util.loads(jsn)["dt"]) # Localtime behind UTC jsn = '{"dt": { "$date" : "1969-12-31T16:00:00.000-0800"}}' self.assertEqual(EPOCH_AWARE, json_util.loads(jsn)["dt"]) + jsn = '{"dt": { "$date" : "1969-12-31T16:00:00.000000-0800"}}' + self.assertEqual(EPOCH_AWARE, json_util.loads(jsn)["dt"]) jsn = '{"dt": { "$date" : "1969-12-31T16:00:00.000-08:00"}}' self.assertEqual(EPOCH_AWARE, json_util.loads(jsn)["dt"]) + jsn = '{"dt": { "$date" : "1969-12-31T16:00:00.000000-08:00"}}' + self.assertEqual(EPOCH_AWARE, json_util.loads(jsn)["dt"]) + jsn = '{"dt": { "$date" : "1969-12-31T16:00:00.000000-08"}}' + self.assertEqual(EPOCH_AWARE, json_util.loads(jsn)["dt"]) # Localtime ahead of UTC jsn = '{"dt": { "$date" : "1970-01-01T01:00:00.000+0100"}}' self.assertEqual(EPOCH_AWARE, json_util.loads(jsn)["dt"]) + jsn = '{"dt": { "$date" : "1970-01-01T01:00:00.000000+0100"}}' + self.assertEqual(EPOCH_AWARE, json_util.loads(jsn)["dt"]) jsn = '{"dt": { "$date" : "1970-01-01T01:00:00.000+01:00"}}' self.assertEqual(EPOCH_AWARE, json_util.loads(jsn)["dt"]) + jsn = '{"dt": { "$date" : "1970-01-01T01:00:00.000000+01:00"}}' + self.assertEqual(EPOCH_AWARE, json_util.loads(jsn)["dt"]) + jsn = '{"dt": { "$date" : "1970-01-01T01:00:00.000000+01"}}' + self.assertEqual(EPOCH_AWARE, json_util.loads(jsn)["dt"]) dtm = datetime.datetime(1, 1, 1, 1, 1, 1, 0, utc) jsn = '{"dt": {"$date": -62135593139000}}'