import datetime import re rfc3339_re = re.compile(r'(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(\.\d+)?(?:Z|([+-]\d{2}):(\d{2}))') def parse_rfc3339(v): m = rfc3339_re.match(v) if not m or m.group(0) != v: return None return parse_rfc3339_re(m) def parse_rfc3339_re(m): r = map(int, m.groups()[:6]) if m.group(7): micro = float(m.group(7)) else: micro = 0 if m.group(8): g = int(m.group(8), 10) * 60 + int(m.group(9), 10) tz = _TimeZone(datetime.timedelta(0, g * 60)) else: tz = _TimeZone(datetime.timedelta(0, 0)) y, m, d, H, M, S = r return datetime.datetime(y, m, d, H, M, S, int(micro * 1000000), tz) def format_rfc3339(v): offs = v.utcoffset() offs = int(offs.total_seconds()) // 60 if offs is not None else 0 if offs == 0: suffix = 'Z' else: if offs > 0: suffix = '+' else: suffix = '-' offs = -offs suffix = '{0}{1:02}:{2:02}'.format(suffix, offs // 60, offs % 60) if v.microsecond: return v.strftime('%Y-%m-%dT%H:%M:%S.%f') + suffix else: return v.strftime('%Y-%m-%dT%H:%M:%S') + suffix class _TimeZone(datetime.tzinfo): def __init__(self, offset): self._offset = offset def utcoffset(self, dt): return self._offset def dst(self, dt): return None def tzname(self, dt): m = self._offset.total_seconds() // 60 if m < 0: res = '-' m = -m else: res = '+' h = m // 60 m = m - h * 60 return '{}{:.02}{:.02}'.format(res, h, m)