1from datetime import datetime, timedelta, timezone, tzinfo
2
3import regex as re
4
5from .timezones import timezone_info_list
6
7
8class StaticTzInfo(tzinfo):
9 def __init__(self, name, offset):
10 self.__offset = offset
11 self.__name = name
12
13 def tzname(self, dt):
14 return self.__name
15
16 def utcoffset(self, dt):
17 return self.__offset
18
19 def dst(self, dt):
20 return timedelta(0)
21
22 def __repr__(self):
23 return "<%s '%s'>" % (self.__class__.__name__, self.__name)
24
25 def localize(self, dt, is_dst=False):
26 if dt.tzinfo is not None:
27 raise ValueError("Not naive datetime (tzinfo is already set)")
28 return dt.replace(tzinfo=self)
29
30 def __getinitargs__(self):
31 return self.__name, self.__offset
32
33
34def pop_tz_offset_from_string(date_string, as_offset=True):
35 if _search_regex_ignorecase.search(date_string):
36 for name, info in _tz_offsets:
37 timezone_re = info["regex"]
38 timezone_match = timezone_re.search(date_string)
39 if timezone_match:
40 start, stop = timezone_match.span()
41 date_string = date_string[: start + 1] + date_string[stop:]
42 return (
43 date_string,
44 StaticTzInfo(name, info["offset"]) if as_offset else name,
45 )
46 return date_string, None
47
48
49def word_is_tz(word):
50 return bool(_search_regex.match(word))
51
52
53def convert_to_local_tz(datetime_obj, datetime_tz_offset):
54 return datetime_obj - datetime_tz_offset + local_tz_offset
55
56
57def build_tz_offsets(search_regex_parts):
58 def get_offset(tz_obj, regex, repl="", replw=""):
59 return (
60 tz_obj[0],
61 {
62 "regex": re.compile(
63 re.sub(repl, replw, regex % tz_obj[0]), re.IGNORECASE
64 ),
65 "offset": timedelta(seconds=tz_obj[1]),
66 },
67 )
68
69 for tz_info in timezone_info_list:
70 for regex in tz_info["regex_patterns"]:
71 for tz_obj in tz_info["timezones"]:
72 search_regex_parts.append(tz_obj[0])
73 yield get_offset(tz_obj, regex)
74
75 # alternate patterns
76 for replace, replacewith in tz_info.get("replace", []):
77 for tz_obj in tz_info["timezones"]:
78 search_regex_parts.append(re.sub(replace, replacewith, tz_obj[0]))
79 yield get_offset(tz_obj, regex, repl=replace, replw=replacewith)
80
81
82def get_local_tz_offset():
83 offset = datetime.now() - datetime.now(tz=timezone.utc).replace(tzinfo=None)
84 offset = timedelta(days=offset.days, seconds=round(offset.seconds, -1))
85 return offset
86
87
88_search_regex_parts = []
89_tz_offsets = list(build_tz_offsets(_search_regex_parts))
90_search_regex = re.compile("|".join(_search_regex_parts))
91_search_regex_ignorecase = re.compile("|".join(_search_regex_parts), re.IGNORECASE)
92local_tz_offset = get_local_tz_offset()