Last active
September 17, 2019 12:52
-
-
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].
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"""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]) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Usage: