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.

140 lines
3.7 KiB

4 years ago
  1. '''
  2. Reference tzinfo implementations from the Python docs.
  3. Used for testing against as they are only correct for the years
  4. 1987 to 2006. Do not use these for real code.
  5. '''
  6. from datetime import tzinfo, timedelta, datetime
  7. from pytz import HOUR, ZERO, UTC
  8. __all__ = [
  9. 'FixedOffset',
  10. 'LocalTimezone',
  11. 'USTimeZone',
  12. 'Eastern',
  13. 'Central',
  14. 'Mountain',
  15. 'Pacific',
  16. 'UTC'
  17. ]
  18. # A class building tzinfo objects for fixed-offset time zones.
  19. # Note that FixedOffset(0, "UTC") is a different way to build a
  20. # UTC tzinfo object.
  21. class FixedOffset(tzinfo):
  22. """Fixed offset in minutes east from UTC."""
  23. def __init__(self, offset, name):
  24. self.__offset = timedelta(minutes=offset)
  25. self.__name = name
  26. def utcoffset(self, dt):
  27. return self.__offset
  28. def tzname(self, dt):
  29. return self.__name
  30. def dst(self, dt):
  31. return ZERO
  32. import time as _time
  33. STDOFFSET = timedelta(seconds=-_time.timezone)
  34. if _time.daylight:
  35. DSTOFFSET = timedelta(seconds=-_time.altzone)
  36. else:
  37. DSTOFFSET = STDOFFSET
  38. DSTDIFF = DSTOFFSET - STDOFFSET
  39. # A class capturing the platform's idea of local time.
  40. class LocalTimezone(tzinfo):
  41. def utcoffset(self, dt):
  42. if self._isdst(dt):
  43. return DSTOFFSET
  44. else:
  45. return STDOFFSET
  46. def dst(self, dt):
  47. if self._isdst(dt):
  48. return DSTDIFF
  49. else:
  50. return ZERO
  51. def tzname(self, dt):
  52. return _time.tzname[self._isdst(dt)]
  53. def _isdst(self, dt):
  54. tt = (dt.year, dt.month, dt.day,
  55. dt.hour, dt.minute, dt.second,
  56. dt.weekday(), 0, -1)
  57. stamp = _time.mktime(tt)
  58. tt = _time.localtime(stamp)
  59. return tt.tm_isdst > 0
  60. Local = LocalTimezone()
  61. def first_sunday_on_or_after(dt):
  62. days_to_go = 6 - dt.weekday()
  63. if days_to_go:
  64. dt += timedelta(days_to_go)
  65. return dt
  66. # In the US, DST starts at 2am (standard time) on the first Sunday in April.
  67. DSTSTART = datetime(1, 4, 1, 2)
  68. # and ends at 2am (DST time; 1am standard time) on the last Sunday of Oct.
  69. # which is the first Sunday on or after Oct 25.
  70. DSTEND = datetime(1, 10, 25, 1)
  71. # A complete implementation of current DST rules for major US time zones.
  72. class USTimeZone(tzinfo):
  73. def __init__(self, hours, reprname, stdname, dstname):
  74. self.stdoffset = timedelta(hours=hours)
  75. self.reprname = reprname
  76. self.stdname = stdname
  77. self.dstname = dstname
  78. def __repr__(self):
  79. return self.reprname
  80. def tzname(self, dt):
  81. if self.dst(dt):
  82. return self.dstname
  83. else:
  84. return self.stdname
  85. def utcoffset(self, dt):
  86. return self.stdoffset + self.dst(dt)
  87. def dst(self, dt):
  88. if dt is None or dt.tzinfo is None:
  89. # An exception may be sensible here, in one or both cases.
  90. # It depends on how you want to treat them. The default
  91. # fromutc() implementation (called by the default astimezone()
  92. # implementation) passes a datetime with dt.tzinfo is self.
  93. return ZERO
  94. assert dt.tzinfo is self
  95. # Find first Sunday in April & the last in October.
  96. start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year))
  97. end = first_sunday_on_or_after(DSTEND.replace(year=dt.year))
  98. # Can't compare naive to aware objects, so strip the timezone from
  99. # dt first.
  100. if start <= dt.replace(tzinfo=None) < end:
  101. return HOUR
  102. else:
  103. return ZERO
  104. Eastern = USTimeZone(-5, "Eastern", "EST", "EDT")
  105. Central = USTimeZone(-6, "Central", "CST", "CDT")
  106. Mountain = USTimeZone(-7, "Mountain", "MST", "MDT")
  107. Pacific = USTimeZone(-8, "Pacific", "PST", "PDT")