annotate CSP2/CSP2_env/env-d9b9114564458d9d-741b3de822f2aaca6c6caa4325c4afce/lib/python3.8/site-packages/pytz/tzinfo.py @ 68:5028fdace37b

planemo upload commit 2e9511a184a1ca667c7be0c6321a36dc4e3d116d
author jpayne
date Tue, 18 Mar 2025 16:23:26 -0400
parents
children
rev   line source
jpayne@68 1 '''Base classes and helpers for building zone specific tzinfo classes'''
jpayne@68 2
jpayne@68 3 from datetime import datetime, timedelta, tzinfo
jpayne@68 4 from bisect import bisect_right
jpayne@68 5 try:
jpayne@68 6 set
jpayne@68 7 except NameError:
jpayne@68 8 from sets import Set as set
jpayne@68 9
jpayne@68 10 import pytz
jpayne@68 11 from pytz.exceptions import AmbiguousTimeError, NonExistentTimeError
jpayne@68 12
jpayne@68 13 __all__ = []
jpayne@68 14
jpayne@68 15 _timedelta_cache = {}
jpayne@68 16
jpayne@68 17
jpayne@68 18 def memorized_timedelta(seconds):
jpayne@68 19 '''Create only one instance of each distinct timedelta'''
jpayne@68 20 try:
jpayne@68 21 return _timedelta_cache[seconds]
jpayne@68 22 except KeyError:
jpayne@68 23 delta = timedelta(seconds=seconds)
jpayne@68 24 _timedelta_cache[seconds] = delta
jpayne@68 25 return delta
jpayne@68 26
jpayne@68 27
jpayne@68 28 _epoch = datetime(1970, 1, 1, 0, 0) # datetime.utcfromtimestamp(0)
jpayne@68 29 _datetime_cache = {0: _epoch}
jpayne@68 30
jpayne@68 31
jpayne@68 32 def memorized_datetime(seconds):
jpayne@68 33 '''Create only one instance of each distinct datetime'''
jpayne@68 34 try:
jpayne@68 35 return _datetime_cache[seconds]
jpayne@68 36 except KeyError:
jpayne@68 37 # NB. We can't just do datetime.fromtimestamp(seconds, tz=timezone.utc).replace(tzinfo=None)
jpayne@68 38 # as this fails with negative values under Windows (Bug #90096)
jpayne@68 39 dt = _epoch + timedelta(seconds=seconds)
jpayne@68 40 _datetime_cache[seconds] = dt
jpayne@68 41 return dt
jpayne@68 42
jpayne@68 43
jpayne@68 44 _ttinfo_cache = {}
jpayne@68 45
jpayne@68 46
jpayne@68 47 def memorized_ttinfo(*args):
jpayne@68 48 '''Create only one instance of each distinct tuple'''
jpayne@68 49 try:
jpayne@68 50 return _ttinfo_cache[args]
jpayne@68 51 except KeyError:
jpayne@68 52 ttinfo = (
jpayne@68 53 memorized_timedelta(args[0]),
jpayne@68 54 memorized_timedelta(args[1]),
jpayne@68 55 args[2]
jpayne@68 56 )
jpayne@68 57 _ttinfo_cache[args] = ttinfo
jpayne@68 58 return ttinfo
jpayne@68 59
jpayne@68 60
jpayne@68 61 _notime = memorized_timedelta(0)
jpayne@68 62
jpayne@68 63
jpayne@68 64 def _to_seconds(td):
jpayne@68 65 '''Convert a timedelta to seconds'''
jpayne@68 66 return td.seconds + td.days * 24 * 60 * 60
jpayne@68 67
jpayne@68 68
jpayne@68 69 class BaseTzInfo(tzinfo):
jpayne@68 70 # Overridden in subclass
jpayne@68 71 _utcoffset = None
jpayne@68 72 _tzname = None
jpayne@68 73 zone = None
jpayne@68 74
jpayne@68 75 def __str__(self):
jpayne@68 76 return self.zone
jpayne@68 77
jpayne@68 78
jpayne@68 79 class StaticTzInfo(BaseTzInfo):
jpayne@68 80 '''A timezone that has a constant offset from UTC
jpayne@68 81
jpayne@68 82 These timezones are rare, as most locations have changed their
jpayne@68 83 offset at some point in their history
jpayne@68 84 '''
jpayne@68 85 def fromutc(self, dt):
jpayne@68 86 '''See datetime.tzinfo.fromutc'''
jpayne@68 87 if dt.tzinfo is not None and dt.tzinfo is not self:
jpayne@68 88 raise ValueError('fromutc: dt.tzinfo is not self')
jpayne@68 89 return (dt + self._utcoffset).replace(tzinfo=self)
jpayne@68 90
jpayne@68 91 def utcoffset(self, dt, is_dst=None):
jpayne@68 92 '''See datetime.tzinfo.utcoffset
jpayne@68 93
jpayne@68 94 is_dst is ignored for StaticTzInfo, and exists only to
jpayne@68 95 retain compatibility with DstTzInfo.
jpayne@68 96 '''
jpayne@68 97 return self._utcoffset
jpayne@68 98
jpayne@68 99 def dst(self, dt, is_dst=None):
jpayne@68 100 '''See datetime.tzinfo.dst
jpayne@68 101
jpayne@68 102 is_dst is ignored for StaticTzInfo, and exists only to
jpayne@68 103 retain compatibility with DstTzInfo.
jpayne@68 104 '''
jpayne@68 105 return _notime
jpayne@68 106
jpayne@68 107 def tzname(self, dt, is_dst=None):
jpayne@68 108 '''See datetime.tzinfo.tzname
jpayne@68 109
jpayne@68 110 is_dst is ignored for StaticTzInfo, and exists only to
jpayne@68 111 retain compatibility with DstTzInfo.
jpayne@68 112 '''
jpayne@68 113 return self._tzname
jpayne@68 114
jpayne@68 115 def localize(self, dt, is_dst=False):
jpayne@68 116 '''Convert naive time to local time'''
jpayne@68 117 if dt.tzinfo is not None:
jpayne@68 118 raise ValueError('Not naive datetime (tzinfo is already set)')
jpayne@68 119 return dt.replace(tzinfo=self)
jpayne@68 120
jpayne@68 121 def normalize(self, dt, is_dst=False):
jpayne@68 122 '''Correct the timezone information on the given datetime.
jpayne@68 123
jpayne@68 124 This is normally a no-op, as StaticTzInfo timezones never have
jpayne@68 125 ambiguous cases to correct:
jpayne@68 126
jpayne@68 127 >>> from pytz import timezone
jpayne@68 128 >>> gmt = timezone('GMT')
jpayne@68 129 >>> isinstance(gmt, StaticTzInfo)
jpayne@68 130 True
jpayne@68 131 >>> dt = datetime(2011, 5, 8, 1, 2, 3, tzinfo=gmt)
jpayne@68 132 >>> gmt.normalize(dt) is dt
jpayne@68 133 True
jpayne@68 134
jpayne@68 135 The supported method of converting between timezones is to use
jpayne@68 136 datetime.astimezone(). Currently normalize() also works:
jpayne@68 137
jpayne@68 138 >>> la = timezone('America/Los_Angeles')
jpayne@68 139 >>> dt = la.localize(datetime(2011, 5, 7, 1, 2, 3))
jpayne@68 140 >>> fmt = '%Y-%m-%d %H:%M:%S %Z (%z)'
jpayne@68 141 >>> gmt.normalize(dt).strftime(fmt)
jpayne@68 142 '2011-05-07 08:02:03 GMT (+0000)'
jpayne@68 143 '''
jpayne@68 144 if dt.tzinfo is self:
jpayne@68 145 return dt
jpayne@68 146 if dt.tzinfo is None:
jpayne@68 147 raise ValueError('Naive time - no tzinfo set')
jpayne@68 148 return dt.astimezone(self)
jpayne@68 149
jpayne@68 150 def __repr__(self):
jpayne@68 151 return '<StaticTzInfo %r>' % (self.zone,)
jpayne@68 152
jpayne@68 153 def __reduce__(self):
jpayne@68 154 # Special pickle to zone remains a singleton and to cope with
jpayne@68 155 # database changes.
jpayne@68 156 return pytz._p, (self.zone,)
jpayne@68 157
jpayne@68 158
jpayne@68 159 class DstTzInfo(BaseTzInfo):
jpayne@68 160 '''A timezone that has a variable offset from UTC
jpayne@68 161
jpayne@68 162 The offset might change if daylight saving time comes into effect,
jpayne@68 163 or at a point in history when the region decides to change their
jpayne@68 164 timezone definition.
jpayne@68 165 '''
jpayne@68 166 # Overridden in subclass
jpayne@68 167
jpayne@68 168 # Sorted list of DST transition times, UTC
jpayne@68 169 _utc_transition_times = None
jpayne@68 170
jpayne@68 171 # [(utcoffset, dstoffset, tzname)] corresponding to
jpayne@68 172 # _utc_transition_times entries
jpayne@68 173 _transition_info = None
jpayne@68 174
jpayne@68 175 zone = None
jpayne@68 176
jpayne@68 177 # Set in __init__
jpayne@68 178
jpayne@68 179 _tzinfos = None
jpayne@68 180 _dst = None # DST offset
jpayne@68 181
jpayne@68 182 def __init__(self, _inf=None, _tzinfos=None):
jpayne@68 183 if _inf:
jpayne@68 184 self._tzinfos = _tzinfos
jpayne@68 185 self._utcoffset, self._dst, self._tzname = _inf
jpayne@68 186 else:
jpayne@68 187 _tzinfos = {}
jpayne@68 188 self._tzinfos = _tzinfos
jpayne@68 189 self._utcoffset, self._dst, self._tzname = (
jpayne@68 190 self._transition_info[0])
jpayne@68 191 _tzinfos[self._transition_info[0]] = self
jpayne@68 192 for inf in self._transition_info[1:]:
jpayne@68 193 if inf not in _tzinfos:
jpayne@68 194 _tzinfos[inf] = self.__class__(inf, _tzinfos)
jpayne@68 195
jpayne@68 196 def fromutc(self, dt):
jpayne@68 197 '''See datetime.tzinfo.fromutc'''
jpayne@68 198 if (dt.tzinfo is not None and
jpayne@68 199 getattr(dt.tzinfo, '_tzinfos', None) is not self._tzinfos):
jpayne@68 200 raise ValueError('fromutc: dt.tzinfo is not self')
jpayne@68 201 dt = dt.replace(tzinfo=None)
jpayne@68 202 idx = max(0, bisect_right(self._utc_transition_times, dt) - 1)
jpayne@68 203 inf = self._transition_info[idx]
jpayne@68 204 return (dt + inf[0]).replace(tzinfo=self._tzinfos[inf])
jpayne@68 205
jpayne@68 206 def normalize(self, dt):
jpayne@68 207 '''Correct the timezone information on the given datetime
jpayne@68 208
jpayne@68 209 If date arithmetic crosses DST boundaries, the tzinfo
jpayne@68 210 is not magically adjusted. This method normalizes the
jpayne@68 211 tzinfo to the correct one.
jpayne@68 212
jpayne@68 213 To test, first we need to do some setup
jpayne@68 214
jpayne@68 215 >>> from pytz import timezone
jpayne@68 216 >>> utc = timezone('UTC')
jpayne@68 217 >>> eastern = timezone('US/Eastern')
jpayne@68 218 >>> fmt = '%Y-%m-%d %H:%M:%S %Z (%z)'
jpayne@68 219
jpayne@68 220 We next create a datetime right on an end-of-DST transition point,
jpayne@68 221 the instant when the wallclocks are wound back one hour.
jpayne@68 222
jpayne@68 223 >>> utc_dt = datetime(2002, 10, 27, 6, 0, 0, tzinfo=utc)
jpayne@68 224 >>> loc_dt = utc_dt.astimezone(eastern)
jpayne@68 225 >>> loc_dt.strftime(fmt)
jpayne@68 226 '2002-10-27 01:00:00 EST (-0500)'
jpayne@68 227
jpayne@68 228 Now, if we subtract a few minutes from it, note that the timezone
jpayne@68 229 information has not changed.
jpayne@68 230
jpayne@68 231 >>> before = loc_dt - timedelta(minutes=10)
jpayne@68 232 >>> before.strftime(fmt)
jpayne@68 233 '2002-10-27 00:50:00 EST (-0500)'
jpayne@68 234
jpayne@68 235 But we can fix that by calling the normalize method
jpayne@68 236
jpayne@68 237 >>> before = eastern.normalize(before)
jpayne@68 238 >>> before.strftime(fmt)
jpayne@68 239 '2002-10-27 01:50:00 EDT (-0400)'
jpayne@68 240
jpayne@68 241 The supported method of converting between timezones is to use
jpayne@68 242 datetime.astimezone(). Currently, normalize() also works:
jpayne@68 243
jpayne@68 244 >>> th = timezone('Asia/Bangkok')
jpayne@68 245 >>> am = timezone('Europe/Amsterdam')
jpayne@68 246 >>> dt = th.localize(datetime(2011, 5, 7, 1, 2, 3))
jpayne@68 247 >>> fmt = '%Y-%m-%d %H:%M:%S %Z (%z)'
jpayne@68 248 >>> am.normalize(dt).strftime(fmt)
jpayne@68 249 '2011-05-06 20:02:03 CEST (+0200)'
jpayne@68 250 '''
jpayne@68 251 if dt.tzinfo is None:
jpayne@68 252 raise ValueError('Naive time - no tzinfo set')
jpayne@68 253
jpayne@68 254 # Convert dt in localtime to UTC
jpayne@68 255 offset = dt.tzinfo._utcoffset
jpayne@68 256 dt = dt.replace(tzinfo=None)
jpayne@68 257 dt = dt - offset
jpayne@68 258 # convert it back, and return it
jpayne@68 259 return self.fromutc(dt)
jpayne@68 260
jpayne@68 261 def localize(self, dt, is_dst=False):
jpayne@68 262 '''Convert naive time to local time.
jpayne@68 263
jpayne@68 264 This method should be used to construct localtimes, rather
jpayne@68 265 than passing a tzinfo argument to a datetime constructor.
jpayne@68 266
jpayne@68 267 is_dst is used to determine the correct timezone in the ambigous
jpayne@68 268 period at the end of daylight saving time.
jpayne@68 269
jpayne@68 270 >>> from pytz import timezone
jpayne@68 271 >>> fmt = '%Y-%m-%d %H:%M:%S %Z (%z)'
jpayne@68 272 >>> amdam = timezone('Europe/Amsterdam')
jpayne@68 273 >>> dt = datetime(2004, 10, 31, 2, 0, 0)
jpayne@68 274 >>> loc_dt1 = amdam.localize(dt, is_dst=True)
jpayne@68 275 >>> loc_dt2 = amdam.localize(dt, is_dst=False)
jpayne@68 276 >>> loc_dt1.strftime(fmt)
jpayne@68 277 '2004-10-31 02:00:00 CEST (+0200)'
jpayne@68 278 >>> loc_dt2.strftime(fmt)
jpayne@68 279 '2004-10-31 02:00:00 CET (+0100)'
jpayne@68 280 >>> str(loc_dt2 - loc_dt1)
jpayne@68 281 '1:00:00'
jpayne@68 282
jpayne@68 283 Use is_dst=None to raise an AmbiguousTimeError for ambiguous
jpayne@68 284 times at the end of daylight saving time
jpayne@68 285
jpayne@68 286 >>> try:
jpayne@68 287 ... loc_dt1 = amdam.localize(dt, is_dst=None)
jpayne@68 288 ... except AmbiguousTimeError:
jpayne@68 289 ... print('Ambiguous')
jpayne@68 290 Ambiguous
jpayne@68 291
jpayne@68 292 is_dst defaults to False
jpayne@68 293
jpayne@68 294 >>> amdam.localize(dt) == amdam.localize(dt, False)
jpayne@68 295 True
jpayne@68 296
jpayne@68 297 is_dst is also used to determine the correct timezone in the
jpayne@68 298 wallclock times jumped over at the start of daylight saving time.
jpayne@68 299
jpayne@68 300 >>> pacific = timezone('US/Pacific')
jpayne@68 301 >>> dt = datetime(2008, 3, 9, 2, 0, 0)
jpayne@68 302 >>> ploc_dt1 = pacific.localize(dt, is_dst=True)
jpayne@68 303 >>> ploc_dt2 = pacific.localize(dt, is_dst=False)
jpayne@68 304 >>> ploc_dt1.strftime(fmt)
jpayne@68 305 '2008-03-09 02:00:00 PDT (-0700)'
jpayne@68 306 >>> ploc_dt2.strftime(fmt)
jpayne@68 307 '2008-03-09 02:00:00 PST (-0800)'
jpayne@68 308 >>> str(ploc_dt2 - ploc_dt1)
jpayne@68 309 '1:00:00'
jpayne@68 310
jpayne@68 311 Use is_dst=None to raise a NonExistentTimeError for these skipped
jpayne@68 312 times.
jpayne@68 313
jpayne@68 314 >>> try:
jpayne@68 315 ... loc_dt1 = pacific.localize(dt, is_dst=None)
jpayne@68 316 ... except NonExistentTimeError:
jpayne@68 317 ... print('Non-existent')
jpayne@68 318 Non-existent
jpayne@68 319 '''
jpayne@68 320 if dt.tzinfo is not None:
jpayne@68 321 raise ValueError('Not naive datetime (tzinfo is already set)')
jpayne@68 322
jpayne@68 323 # Find the two best possibilities.
jpayne@68 324 possible_loc_dt = set()
jpayne@68 325 for delta in [timedelta(days=-1), timedelta(days=1)]:
jpayne@68 326 loc_dt = dt + delta
jpayne@68 327 idx = max(0, bisect_right(
jpayne@68 328 self._utc_transition_times, loc_dt) - 1)
jpayne@68 329 inf = self._transition_info[idx]
jpayne@68 330 tzinfo = self._tzinfos[inf]
jpayne@68 331 loc_dt = tzinfo.normalize(dt.replace(tzinfo=tzinfo))
jpayne@68 332 if loc_dt.replace(tzinfo=None) == dt:
jpayne@68 333 possible_loc_dt.add(loc_dt)
jpayne@68 334
jpayne@68 335 if len(possible_loc_dt) == 1:
jpayne@68 336 return possible_loc_dt.pop()
jpayne@68 337
jpayne@68 338 # If there are no possibly correct timezones, we are attempting
jpayne@68 339 # to convert a time that never happened - the time period jumped
jpayne@68 340 # during the start-of-DST transition period.
jpayne@68 341 if len(possible_loc_dt) == 0:
jpayne@68 342 # If we refuse to guess, raise an exception.
jpayne@68 343 if is_dst is None:
jpayne@68 344 raise NonExistentTimeError(dt)
jpayne@68 345
jpayne@68 346 # If we are forcing the pre-DST side of the DST transition, we
jpayne@68 347 # obtain the correct timezone by winding the clock forward a few
jpayne@68 348 # hours.
jpayne@68 349 elif is_dst:
jpayne@68 350 return self.localize(
jpayne@68 351 dt + timedelta(hours=6), is_dst=True) - timedelta(hours=6)
jpayne@68 352
jpayne@68 353 # If we are forcing the post-DST side of the DST transition, we
jpayne@68 354 # obtain the correct timezone by winding the clock back.
jpayne@68 355 else:
jpayne@68 356 return self.localize(
jpayne@68 357 dt - timedelta(hours=6),
jpayne@68 358 is_dst=False) + timedelta(hours=6)
jpayne@68 359
jpayne@68 360 # If we get this far, we have multiple possible timezones - this
jpayne@68 361 # is an ambiguous case occurring during the end-of-DST transition.
jpayne@68 362
jpayne@68 363 # If told to be strict, raise an exception since we have an
jpayne@68 364 # ambiguous case
jpayne@68 365 if is_dst is None:
jpayne@68 366 raise AmbiguousTimeError(dt)
jpayne@68 367
jpayne@68 368 # Filter out the possiblilities that don't match the requested
jpayne@68 369 # is_dst
jpayne@68 370 filtered_possible_loc_dt = [
jpayne@68 371 p for p in possible_loc_dt if bool(p.tzinfo._dst) == is_dst
jpayne@68 372 ]
jpayne@68 373
jpayne@68 374 # Hopefully we only have one possibility left. Return it.
jpayne@68 375 if len(filtered_possible_loc_dt) == 1:
jpayne@68 376 return filtered_possible_loc_dt[0]
jpayne@68 377
jpayne@68 378 if len(filtered_possible_loc_dt) == 0:
jpayne@68 379 filtered_possible_loc_dt = list(possible_loc_dt)
jpayne@68 380
jpayne@68 381 # If we get this far, we have in a wierd timezone transition
jpayne@68 382 # where the clocks have been wound back but is_dst is the same
jpayne@68 383 # in both (eg. Europe/Warsaw 1915 when they switched to CET).
jpayne@68 384 # At this point, we just have to guess unless we allow more
jpayne@68 385 # hints to be passed in (such as the UTC offset or abbreviation),
jpayne@68 386 # but that is just getting silly.
jpayne@68 387 #
jpayne@68 388 # Choose the earliest (by UTC) applicable timezone if is_dst=True
jpayne@68 389 # Choose the latest (by UTC) applicable timezone if is_dst=False
jpayne@68 390 # i.e., behave like end-of-DST transition
jpayne@68 391 dates = {} # utc -> local
jpayne@68 392 for local_dt in filtered_possible_loc_dt:
jpayne@68 393 utc_time = (
jpayne@68 394 local_dt.replace(tzinfo=None) - local_dt.tzinfo._utcoffset)
jpayne@68 395 assert utc_time not in dates
jpayne@68 396 dates[utc_time] = local_dt
jpayne@68 397 return dates[[min, max][not is_dst](dates)]
jpayne@68 398
jpayne@68 399 def utcoffset(self, dt, is_dst=None):
jpayne@68 400 '''See datetime.tzinfo.utcoffset
jpayne@68 401
jpayne@68 402 The is_dst parameter may be used to remove ambiguity during DST
jpayne@68 403 transitions.
jpayne@68 404
jpayne@68 405 >>> from pytz import timezone
jpayne@68 406 >>> tz = timezone('America/St_Johns')
jpayne@68 407 >>> ambiguous = datetime(2009, 10, 31, 23, 30)
jpayne@68 408
jpayne@68 409 >>> str(tz.utcoffset(ambiguous, is_dst=False))
jpayne@68 410 '-1 day, 20:30:00'
jpayne@68 411
jpayne@68 412 >>> str(tz.utcoffset(ambiguous, is_dst=True))
jpayne@68 413 '-1 day, 21:30:00'
jpayne@68 414
jpayne@68 415 >>> try:
jpayne@68 416 ... tz.utcoffset(ambiguous)
jpayne@68 417 ... except AmbiguousTimeError:
jpayne@68 418 ... print('Ambiguous')
jpayne@68 419 Ambiguous
jpayne@68 420
jpayne@68 421 '''
jpayne@68 422 if dt is None:
jpayne@68 423 return None
jpayne@68 424 elif dt.tzinfo is not self:
jpayne@68 425 dt = self.localize(dt, is_dst)
jpayne@68 426 return dt.tzinfo._utcoffset
jpayne@68 427 else:
jpayne@68 428 return self._utcoffset
jpayne@68 429
jpayne@68 430 def dst(self, dt, is_dst=None):
jpayne@68 431 '''See datetime.tzinfo.dst
jpayne@68 432
jpayne@68 433 The is_dst parameter may be used to remove ambiguity during DST
jpayne@68 434 transitions.
jpayne@68 435
jpayne@68 436 >>> from pytz import timezone
jpayne@68 437 >>> tz = timezone('America/St_Johns')
jpayne@68 438
jpayne@68 439 >>> normal = datetime(2009, 9, 1)
jpayne@68 440
jpayne@68 441 >>> str(tz.dst(normal))
jpayne@68 442 '1:00:00'
jpayne@68 443 >>> str(tz.dst(normal, is_dst=False))
jpayne@68 444 '1:00:00'
jpayne@68 445 >>> str(tz.dst(normal, is_dst=True))
jpayne@68 446 '1:00:00'
jpayne@68 447
jpayne@68 448 >>> ambiguous = datetime(2009, 10, 31, 23, 30)
jpayne@68 449
jpayne@68 450 >>> str(tz.dst(ambiguous, is_dst=False))
jpayne@68 451 '0:00:00'
jpayne@68 452 >>> str(tz.dst(ambiguous, is_dst=True))
jpayne@68 453 '1:00:00'
jpayne@68 454 >>> try:
jpayne@68 455 ... tz.dst(ambiguous)
jpayne@68 456 ... except AmbiguousTimeError:
jpayne@68 457 ... print('Ambiguous')
jpayne@68 458 Ambiguous
jpayne@68 459
jpayne@68 460 '''
jpayne@68 461 if dt is None:
jpayne@68 462 return None
jpayne@68 463 elif dt.tzinfo is not self:
jpayne@68 464 dt = self.localize(dt, is_dst)
jpayne@68 465 return dt.tzinfo._dst
jpayne@68 466 else:
jpayne@68 467 return self._dst
jpayne@68 468
jpayne@68 469 def tzname(self, dt, is_dst=None):
jpayne@68 470 '''See datetime.tzinfo.tzname
jpayne@68 471
jpayne@68 472 The is_dst parameter may be used to remove ambiguity during DST
jpayne@68 473 transitions.
jpayne@68 474
jpayne@68 475 >>> from pytz import timezone
jpayne@68 476 >>> tz = timezone('America/St_Johns')
jpayne@68 477
jpayne@68 478 >>> normal = datetime(2009, 9, 1)
jpayne@68 479
jpayne@68 480 >>> tz.tzname(normal)
jpayne@68 481 'NDT'
jpayne@68 482 >>> tz.tzname(normal, is_dst=False)
jpayne@68 483 'NDT'
jpayne@68 484 >>> tz.tzname(normal, is_dst=True)
jpayne@68 485 'NDT'
jpayne@68 486
jpayne@68 487 >>> ambiguous = datetime(2009, 10, 31, 23, 30)
jpayne@68 488
jpayne@68 489 >>> tz.tzname(ambiguous, is_dst=False)
jpayne@68 490 'NST'
jpayne@68 491 >>> tz.tzname(ambiguous, is_dst=True)
jpayne@68 492 'NDT'
jpayne@68 493 >>> try:
jpayne@68 494 ... tz.tzname(ambiguous)
jpayne@68 495 ... except AmbiguousTimeError:
jpayne@68 496 ... print('Ambiguous')
jpayne@68 497 Ambiguous
jpayne@68 498 '''
jpayne@68 499 if dt is None:
jpayne@68 500 return self.zone
jpayne@68 501 elif dt.tzinfo is not self:
jpayne@68 502 dt = self.localize(dt, is_dst)
jpayne@68 503 return dt.tzinfo._tzname
jpayne@68 504 else:
jpayne@68 505 return self._tzname
jpayne@68 506
jpayne@68 507 def __repr__(self):
jpayne@68 508 if self._dst:
jpayne@68 509 dst = 'DST'
jpayne@68 510 else:
jpayne@68 511 dst = 'STD'
jpayne@68 512 if self._utcoffset > _notime:
jpayne@68 513 return '<DstTzInfo %r %s+%s %s>' % (
jpayne@68 514 self.zone, self._tzname, self._utcoffset, dst
jpayne@68 515 )
jpayne@68 516 else:
jpayne@68 517 return '<DstTzInfo %r %s%s %s>' % (
jpayne@68 518 self.zone, self._tzname, self._utcoffset, dst
jpayne@68 519 )
jpayne@68 520
jpayne@68 521 def __reduce__(self):
jpayne@68 522 # Special pickle to zone remains a singleton and to cope with
jpayne@68 523 # database changes.
jpayne@68 524 return pytz._p, (
jpayne@68 525 self.zone,
jpayne@68 526 _to_seconds(self._utcoffset),
jpayne@68 527 _to_seconds(self._dst),
jpayne@68 528 self._tzname
jpayne@68 529 )
jpayne@68 530
jpayne@68 531
jpayne@68 532 def unpickler(zone, utcoffset=None, dstoffset=None, tzname=None):
jpayne@68 533 """Factory function for unpickling pytz tzinfo instances.
jpayne@68 534
jpayne@68 535 This is shared for both StaticTzInfo and DstTzInfo instances, because
jpayne@68 536 database changes could cause a zones implementation to switch between
jpayne@68 537 these two base classes and we can't break pickles on a pytz version
jpayne@68 538 upgrade.
jpayne@68 539 """
jpayne@68 540 # Raises a KeyError if zone no longer exists, which should never happen
jpayne@68 541 # and would be a bug.
jpayne@68 542 tz = pytz.timezone(zone)
jpayne@68 543
jpayne@68 544 # A StaticTzInfo - just return it
jpayne@68 545 if utcoffset is None:
jpayne@68 546 return tz
jpayne@68 547
jpayne@68 548 # This pickle was created from a DstTzInfo. We need to
jpayne@68 549 # determine which of the list of tzinfo instances for this zone
jpayne@68 550 # to use in order to restore the state of any datetime instances using
jpayne@68 551 # it correctly.
jpayne@68 552 utcoffset = memorized_timedelta(utcoffset)
jpayne@68 553 dstoffset = memorized_timedelta(dstoffset)
jpayne@68 554 try:
jpayne@68 555 return tz._tzinfos[(utcoffset, dstoffset, tzname)]
jpayne@68 556 except KeyError:
jpayne@68 557 # The particular state requested in this timezone no longer exists.
jpayne@68 558 # This indicates a corrupt pickle, or the timezone database has been
jpayne@68 559 # corrected violently enough to make this particular
jpayne@68 560 # (utcoffset,dstoffset) no longer exist in the zone, or the
jpayne@68 561 # abbreviation has been changed.
jpayne@68 562 pass
jpayne@68 563
jpayne@68 564 # See if we can find an entry differing only by tzname. Abbreviations
jpayne@68 565 # get changed from the initial guess by the database maintainers to
jpayne@68 566 # match reality when this information is discovered.
jpayne@68 567 for localized_tz in tz._tzinfos.values():
jpayne@68 568 if (localized_tz._utcoffset == utcoffset and
jpayne@68 569 localized_tz._dst == dstoffset):
jpayne@68 570 return localized_tz
jpayne@68 571
jpayne@68 572 # This (utcoffset, dstoffset) information has been removed from the
jpayne@68 573 # zone. Add it back. This might occur when the database maintainers have
jpayne@68 574 # corrected incorrect information. datetime instances using this
jpayne@68 575 # incorrect information will continue to do so, exactly as they were
jpayne@68 576 # before being pickled. This is purely an overly paranoid safety net - I
jpayne@68 577 # doubt this will ever been needed in real life.
jpayne@68 578 inf = (utcoffset, dstoffset, tzname)
jpayne@68 579 tz._tzinfos[inf] = tz.__class__(inf, tz._tzinfos)
jpayne@68 580 return tz._tzinfos[inf]