tz.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. """tzinfo implementations for psycopg2
  2. This module holds two different tzinfo implementations that can be used as
  3. the 'tzinfo' argument to datetime constructors, directly passed to psycopg
  4. functions or used to set the .tzinfo_factory attribute in cursors.
  5. """
  6. # psycopg/tz.py - tzinfo implementation
  7. #
  8. # Copyright (C) 2003-2019 Federico Di Gregorio <fog@debian.org>
  9. # Copyright (C) 2020 The Psycopg Team
  10. #
  11. # psycopg2 is free software: you can redistribute it and/or modify it
  12. # under the terms of the GNU Lesser General Public License as published
  13. # by the Free Software Foundation, either version 3 of the License, or
  14. # (at your option) any later version.
  15. #
  16. # In addition, as a special exception, the copyright holders give
  17. # permission to link this program with the OpenSSL library (or with
  18. # modified versions of OpenSSL that use the same license as OpenSSL),
  19. # and distribute linked combinations including the two.
  20. #
  21. # You must obey the GNU Lesser General Public License in all respects for
  22. # all of the code used other than OpenSSL.
  23. #
  24. # psycopg2 is distributed in the hope that it will be useful, but WITHOUT
  25. # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  26. # FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
  27. # License for more details.
  28. import datetime
  29. import time
  30. ZERO = datetime.timedelta(0)
  31. class FixedOffsetTimezone(datetime.tzinfo):
  32. """Fixed offset in minutes east from UTC.
  33. This is exactly the implementation__ found in Python 2.3.x documentation,
  34. with a small change to the `!__init__()` method to allow for pickling
  35. and a default name in the form ``sHH:MM`` (``s`` is the sign.).
  36. The implementation also caches instances. During creation, if a
  37. FixedOffsetTimezone instance has previously been created with the same
  38. offset and name that instance will be returned. This saves memory and
  39. improves comparability.
  40. .. __: https://docs.python.org/library/datetime.html
  41. """
  42. _name = None
  43. _offset = ZERO
  44. _cache = {}
  45. def __init__(self, offset=None, name=None):
  46. if offset is not None:
  47. self._offset = datetime.timedelta(minutes=offset)
  48. if name is not None:
  49. self._name = name
  50. def __new__(cls, offset=None, name=None):
  51. """Return a suitable instance created earlier if it exists
  52. """
  53. key = (offset, name)
  54. try:
  55. return cls._cache[key]
  56. except KeyError:
  57. tz = super(FixedOffsetTimezone, cls).__new__(cls, offset, name)
  58. cls._cache[key] = tz
  59. return tz
  60. def __repr__(self):
  61. offset_mins = self._offset.seconds // 60 + self._offset.days * 24 * 60
  62. return "psycopg2.tz.FixedOffsetTimezone(offset=%r, name=%r)" \
  63. % (offset_mins, self._name)
  64. def __getinitargs__(self):
  65. offset_mins = self._offset.seconds // 60 + self._offset.days * 24 * 60
  66. return offset_mins, self._name
  67. def utcoffset(self, dt):
  68. return self._offset
  69. def tzname(self, dt):
  70. if self._name is not None:
  71. return self._name
  72. else:
  73. seconds = self._offset.seconds + self._offset.days * 86400
  74. hours, seconds = divmod(seconds, 3600)
  75. minutes = seconds / 60
  76. if minutes:
  77. return "%+03d:%d" % (hours, minutes)
  78. else:
  79. return "%+03d" % hours
  80. def dst(self, dt):
  81. return ZERO
  82. STDOFFSET = datetime.timedelta(seconds=-time.timezone)
  83. if time.daylight:
  84. DSTOFFSET = datetime.timedelta(seconds=-time.altzone)
  85. else:
  86. DSTOFFSET = STDOFFSET
  87. DSTDIFF = DSTOFFSET - STDOFFSET
  88. class LocalTimezone(datetime.tzinfo):
  89. """Platform idea of local timezone.
  90. This is the exact implementation from the Python 2.3 documentation.
  91. """
  92. def utcoffset(self, dt):
  93. if self._isdst(dt):
  94. return DSTOFFSET
  95. else:
  96. return STDOFFSET
  97. def dst(self, dt):
  98. if self._isdst(dt):
  99. return DSTDIFF
  100. else:
  101. return ZERO
  102. def tzname(self, dt):
  103. return time.tzname[self._isdst(dt)]
  104. def _isdst(self, dt):
  105. tt = (dt.year, dt.month, dt.day,
  106. dt.hour, dt.minute, dt.second,
  107. dt.weekday(), 0, -1)
  108. stamp = time.mktime(tt)
  109. tt = time.localtime(stamp)
  110. return tt.tm_isdst > 0
  111. LOCAL = LocalTimezone()
  112. # TODO: pre-generate some interesting time zones?