You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

67 lines
1.7 KiB

4 years ago
  1. import datetime
  2. import re
  3. rfc3339_re = re.compile(r'(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(\.\d+)?(?:Z|([+-]\d{2}):(\d{2}))')
  4. def parse_rfc3339(v):
  5. m = rfc3339_re.match(v)
  6. if not m or m.group(0) != v:
  7. return None
  8. return parse_rfc3339_re(m)
  9. def parse_rfc3339_re(m):
  10. r = map(int, m.groups()[:6])
  11. if m.group(7):
  12. micro = float(m.group(7))
  13. else:
  14. micro = 0
  15. if m.group(8):
  16. g = int(m.group(8), 10) * 60 + int(m.group(9), 10)
  17. tz = _TimeZone(datetime.timedelta(0, g * 60))
  18. else:
  19. tz = _TimeZone(datetime.timedelta(0, 0))
  20. y, m, d, H, M, S = r
  21. return datetime.datetime(y, m, d, H, M, S, int(micro * 1000000), tz)
  22. def format_rfc3339(v):
  23. offs = v.utcoffset()
  24. offs = int(offs.total_seconds()) // 60 if offs is not None else 0
  25. if offs == 0:
  26. suffix = 'Z'
  27. else:
  28. if offs > 0:
  29. suffix = '+'
  30. else:
  31. suffix = '-'
  32. offs = -offs
  33. suffix = '{0}{1:02}:{2:02}'.format(suffix, offs // 60, offs % 60)
  34. if v.microsecond:
  35. return v.strftime('%Y-%m-%dT%H:%M:%S.%f') + suffix
  36. else:
  37. return v.strftime('%Y-%m-%dT%H:%M:%S') + suffix
  38. class _TimeZone(datetime.tzinfo):
  39. def __init__(self, offset):
  40. self._offset = offset
  41. def utcoffset(self, dt):
  42. return self._offset
  43. def dst(self, dt):
  44. return None
  45. def tzname(self, dt):
  46. m = self._offset.total_seconds() // 60
  47. if m < 0:
  48. res = '-'
  49. m = -m
  50. else:
  51. res = '+'
  52. h = m // 60
  53. m = m - h * 60
  54. return '{}{:.02}{:.02}'.format(res, h, m)