py2_compat.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. import datetime
  2. from email._parseaddr import _daynames, _monthnames, _timezones
  3. def _parsedate_tz(data):
  4. """Convert date to extended time tuple.
  5. The last (additional) element is the time zone offset in seconds, except if
  6. the timezone was specified as -0000. In that case the last element is
  7. None. This indicates a UTC timestamp that explicitly declaims knowledge of
  8. the source timezone, as opposed to a +0000 timestamp that indicates the
  9. source timezone really was UTC.
  10. """
  11. if not data:
  12. return
  13. data = data.split()
  14. # The FWS after the comma after the day-of-week is optional, so search and
  15. # adjust for this.
  16. if data[0].endswith(',') or data[0].lower() in _daynames:
  17. # There's a dayname here. Skip it
  18. del data[0]
  19. else:
  20. i = data[0].rfind(',')
  21. if i >= 0:
  22. data[0] = data[0][i+1:]
  23. if len(data) == 3: # RFC 850 date, deprecated
  24. stuff = data[0].split('-')
  25. if len(stuff) == 3:
  26. data = stuff + data[1:]
  27. if len(data) == 4:
  28. s = data[3]
  29. i = s.find('+')
  30. if i == -1:
  31. i = s.find('-')
  32. if i > 0:
  33. data[3:] = [s[:i], s[i:]]
  34. else:
  35. data.append('') # Dummy tz
  36. if len(data) < 5:
  37. return None
  38. data = data[:5]
  39. [dd, mm, yy, tm, tz] = data
  40. mm = mm.lower()
  41. if mm not in _monthnames:
  42. dd, mm = mm, dd.lower()
  43. if mm not in _monthnames:
  44. return None
  45. mm = _monthnames.index(mm) + 1
  46. if mm > 12:
  47. mm -= 12
  48. if dd[-1] == ',':
  49. dd = dd[:-1]
  50. i = yy.find(':')
  51. if i > 0:
  52. yy, tm = tm, yy
  53. if yy[-1] == ',':
  54. yy = yy[:-1]
  55. if not yy[0].isdigit():
  56. yy, tz = tz, yy
  57. if tm[-1] == ',':
  58. tm = tm[:-1]
  59. tm = tm.split(':')
  60. if len(tm) == 2:
  61. [thh, tmm] = tm
  62. tss = '0'
  63. elif len(tm) == 3:
  64. [thh, tmm, tss] = tm
  65. elif len(tm) == 1 and '.' in tm[0]:
  66. # Some non-compliant MUAs use '.' to separate time elements.
  67. tm = tm[0].split('.')
  68. if len(tm) == 2:
  69. [thh, tmm] = tm
  70. tss = 0
  71. elif len(tm) == 3:
  72. [thh, tmm, tss] = tm
  73. else:
  74. return None
  75. try:
  76. yy = int(yy)
  77. dd = int(dd)
  78. thh = int(thh)
  79. tmm = int(tmm)
  80. tss = int(tss)
  81. except ValueError:
  82. return None
  83. # Check for a yy specified in two-digit format, then convert it to the
  84. # appropriate four-digit format, according to the POSIX standard. RFC 822
  85. # calls for a two-digit yy, but RFC 2822 (which obsoletes RFC 822)
  86. # mandates a 4-digit yy. For more information, see the documentation for
  87. # the time module.
  88. if yy < 100:
  89. # The year is between 1969 and 1999 (inclusive).
  90. if yy > 68:
  91. yy += 1900
  92. # The year is between 2000 and 2068 (inclusive).
  93. else:
  94. yy += 2000
  95. tzoffset = None
  96. tz = tz.upper()
  97. if tz in _timezones:
  98. tzoffset = _timezones[tz]
  99. else:
  100. try:
  101. tzoffset = int(tz)
  102. except ValueError:
  103. pass
  104. if tzoffset==0 and tz.startswith('-'):
  105. tzoffset = None
  106. # Convert a timezone offset into seconds ; -0500 -> -18000
  107. if tzoffset:
  108. if tzoffset < 0:
  109. tzsign = -1
  110. tzoffset = -tzoffset
  111. else:
  112. tzsign = 1
  113. tzoffset = tzsign * ( (tzoffset//100)*3600 + (tzoffset % 100)*60)
  114. # Daylight Saving Time flag is set to -1, since DST is unknown.
  115. return [yy, mm, dd, thh, tmm, tss, 0, 1, -1, tzoffset]
  116. def parsedate_to_datetime(data):
  117. dtuple, tz = _parsedate_tz(data)
  118. if tz is None:
  119. return datetime.datetime(*dtuple[:6])
  120. return datetime.datetime(
  121. *dtuple[:6],
  122. tzinfo=datetime.timezone(datetime.timedelta(seconds=tz)))