Skip to content

Instantly share code, notes, and snippets.

@mdrachuk
Last active September 17, 2019 12:52
Show Gist options
  • Save mdrachuk/72d1bbc8f78bd167271b726542ebe7cc to your computer and use it in GitHub Desktop.
Save mdrachuk/72d1bbc8f78bd167271b726542ebe7cc to your computer and use it in GitHub Desktop.
An immutable tzinfo implementation with *nice* offsets: utc[+2:00], utc[+1:30].
"""TimeZone is an immutable (frozen) tzinfo implementation with *nice* offsets: utc[+2:00], utc[+1:30]."""
__author__ = "Misha Drachuk"
__license__ = "MIT"
from dataclasses import dataclass, field
from datetime import timezone, timedelta, tzinfo, datetime
from math import copysign
@dataclass(frozen=True)
class TimeZone(tzinfo):
"""A timezone object that can be converted to other timezones: utc[+2:30], utc[-7], etc."""
name: str = field(hash=False, compare=False)
tz: tzinfo = field(repr=False, default=timezone.utc)
def __getitem__(self, difference):
if isinstance(difference, slice):
if difference.step:
hours, minutes, seconds = difference.start, difference.stop, difference.step
else:
hours, minutes = difference.start, difference.stop
seconds = 0
else:
hours = difference
minutes = 0
seconds = 0
assert -13 <= hours <= +14
assert 0 <= minutes < 60
assert 0 <= seconds < 60
if not isinstance(hours, int) or not isinstance(minutes, int) or not isinstance(seconds, int):
raise ValueError('Timezone can only be specified by integer values.')
minutes = int(copysign(minutes, hours))
seconds = int(copysign(seconds, hours))
return TimeZone(
f'{self.name}[{hours:0>+2d}:{minutes:0>2d}:{seconds:0>2d}]',
timezone(self.tz.utcoffset(None) + timedelta(hours=hours, minutes=minutes, seconds=seconds))
)
"tzinfo interface implementation"
def tzname(self, dt):
return self.tz.tzname(dt)
def utcoffset(self, dt):
return self.tz.utcoffset(dt)
def dst(self, dt):
return self.tz.dst(dt)
"hints for object misuse"
def __add__(self, other):
raise NotImplementedError(f'Use square brackets instead. E.g. "gmt[+3]", "gmt[+2:30]".')
def __sub__(self, other):
raise NotImplementedError(f'Use square brackets instead. E.g. gmt[-2:30], gmt[-3], etc.')
gmt = TimeZone('gmt')
pdt = TimeZone('pdt', gmt[-7:00])
@mdrachuk
Copy link
Author

Usage:

>>> from tz import gmt, pdt

>>> print(gmt[-2:30])
TimeZone(name='gmt[-2:30:00]')

>>> print(gmt[+4])
TimeZone(name='gmt[+4:00:00]')

>>> datetime(2020, 4, 20, 16, 20, tzinfo=pdt)
2020-04-20 16:20:00-07:00

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment