|
8 | 8 |
|
9 | 9 | pytz >=2019.1
|
10 | 10 |
|
11 |
| -
|
12 |
| -.. warning:: This module has not been fully tested. Use with caution. |
13 |
| -
|
14 | 11 | """
|
15 | 12 | #
|
16 | 13 | # Copyright © 2020 Dominic Davis-Foster <[email protected]>
|
|
47 | 44 | import sys
|
48 | 45 | import typing
|
49 | 46 | from collections import OrderedDict
|
| 47 | +from types import ModuleType |
50 | 48 | from typing import Optional, Union
|
51 | 49 |
|
52 | 50 | __all__ = [
|
|
60 | 58 | "calc_easter",
|
61 | 59 | ]
|
62 | 60 |
|
63 |
| -try: |
64 |
| - |
65 |
| - # 3rd party |
66 |
| - import pytz |
67 |
| - |
68 |
| - def get_utc_offset( |
69 |
| - tz: Union[datetime.tzinfo, str], |
70 |
| - date: Optional[datetime.datetime] = None, |
71 |
| - ) -> Optional[datetime.timedelta]: |
72 |
| - """ |
73 |
| - Returns the offset between UTC and the requested timezone on the given date. |
74 |
| - If ``date`` is :py:obj:`None` then the current date is used. |
75 |
| -
|
76 |
| - :param tz: ``pytz.timezone`` or a string representing the timezone |
77 |
| - :param date: The date to obtain the UTC offset for |
78 |
| - """ |
79 |
| - |
80 |
| - if date is None: |
81 |
| - date = datetime.datetime.utcnow() |
82 |
| - |
83 |
| - timezone: Optional[datetime.tzinfo] |
84 |
| - |
85 |
| - if isinstance(tz, str): |
86 |
| - timezone = get_timezone(tz, date) |
87 |
| - else: |
88 |
| - timezone = tz # pragma: no cover (hard to test) |
89 |
| - |
90 |
| - return date.replace(tzinfo=pytz.utc).astimezone(timezone).utcoffset() |
91 |
| - |
92 |
| - def get_timezone(tz: str, date: Optional[datetime.datetime] = None) -> Optional[datetime.tzinfo]: |
93 |
| - """ |
94 |
| - Returns a localized ``pytz.timezone`` object for the given date. |
95 |
| - If ``date`` is :py:obj:`None` then the current date is used. |
96 |
| -
|
97 |
| - :param tz: A string representing a pytz timezone |
98 |
| - :param date: The date to obtain the timezone for |
99 |
| - """ |
100 |
| - |
101 |
| - if date is None: |
102 |
| - date = datetime.datetime.utcnow() # pragma: no cover (hard to test) |
103 |
| - |
104 |
| - d = date.replace(tzinfo=None) |
105 |
| - |
106 |
| - return pytz.timezone(tz).localize(d).tzinfo |
107 |
| - |
108 |
| - __all__.extend(["get_utc_offset", "get_timezone"]) |
109 |
| - |
110 |
| -except ImportError as e: # pragma: no cover |
111 |
| - |
112 |
| - # stdlib |
113 |
| - import warnings |
114 |
| - |
115 |
| - warnings.warn( |
116 |
| - f"""\ |
117 |
| -Some functions in 'domdf_python_tools.dates' require pytz (https://pypi.org/project/pytz/), \ |
118 |
| -but it could not be imported. |
119 |
| -
|
120 |
| -The error was: {e}. |
121 |
| -""", |
122 |
| - ) |
123 |
| - |
124 | 61 |
|
125 | 62 | def current_tzinfo() -> Optional[datetime.tzinfo]:
|
126 | 63 | """
|
@@ -192,9 +129,9 @@ def utc_timestamp_to_datetime(
|
192 | 129 | return new_datetime.astimezone(output_tz)
|
193 | 130 |
|
194 | 131 |
|
195 |
| -if sys.version_info <= (3, 7, 2): |
| 132 | +if sys.version_info <= (3, 7, 2): # pragma: no cover (py37+) |
196 | 133 | MonthsType = OrderedDict
|
197 |
| -else: |
| 134 | +else: # pragma: no cover (<py37) |
198 | 135 | MonthsType = typing.OrderedDict[str, str] # type: ignore # noqa: TYP006
|
199 | 136 |
|
200 | 137 | #: Mapping of 3-character shortcodes to full month names.
|
@@ -302,3 +239,82 @@ def calc_easter(year: int) -> datetime.date:
|
302 | 239 | day = f % 31 + 1
|
303 | 240 |
|
304 | 241 | return datetime.date(year, month, day)
|
| 242 | + |
| 243 | + |
| 244 | +def get_utc_offset( |
| 245 | + tz: Union[datetime.tzinfo, str], |
| 246 | + date: Optional[datetime.datetime] = None, |
| 247 | + ) -> Optional[datetime.timedelta]: |
| 248 | + """ |
| 249 | + Returns the offset between UTC and the requested timezone on the given date. |
| 250 | + If ``date`` is :py:obj:`None` then the current date is used. |
| 251 | +
|
| 252 | + :param tz: ``pytz.timezone`` or a string representing the timezone |
| 253 | + :param date: The date to obtain the UTC offset for |
| 254 | + """ |
| 255 | + |
| 256 | + if date is None: |
| 257 | + date = datetime.datetime.utcnow() |
| 258 | + |
| 259 | + timezone: Optional[datetime.tzinfo] |
| 260 | + |
| 261 | + if isinstance(tz, str): |
| 262 | + timezone = get_timezone(tz, date) |
| 263 | + else: |
| 264 | + timezone = tz # pragma: no cover (hard to test) |
| 265 | + |
| 266 | + return date.replace(tzinfo=pytz.utc).astimezone(timezone).utcoffset() |
| 267 | + |
| 268 | + |
| 269 | +def get_timezone(tz: str, date: Optional[datetime.datetime] = None) -> Optional[datetime.tzinfo]: |
| 270 | + """ |
| 271 | + Returns a localized ``pytz.timezone`` object for the given date. |
| 272 | + If ``date`` is :py:obj:`None` then the current date is used. |
| 273 | +
|
| 274 | + :param tz: A string representing a pytz timezone |
| 275 | + :param date: The date to obtain the timezone for |
| 276 | + """ |
| 277 | + |
| 278 | + if date is None: |
| 279 | + date = datetime.datetime.utcnow() # pragma: no cover (hard to test) |
| 280 | + |
| 281 | + d = date.replace(tzinfo=None) |
| 282 | + |
| 283 | + return pytz.timezone(tz).localize(d).tzinfo |
| 284 | + |
| 285 | + |
| 286 | +_pytz_functions = ["get_utc_offset", "get_timezone"] |
| 287 | + |
| 288 | +try: |
| 289 | + |
| 290 | + # 3rd party |
| 291 | + import pytz |
| 292 | + |
| 293 | + __all__.extend(_pytz_functions) |
| 294 | + |
| 295 | +except ImportError as e: |
| 296 | + |
| 297 | + if __name__ == "__main__": |
| 298 | + |
| 299 | + import warnings |
| 300 | + from domdf_python_tools.words import word_join |
| 301 | + |
| 302 | + warnings.warn(f"""\ |
| 303 | + '{word_join(_pytz_functions)}' require pytz (https://pypi.org/project/pytz/), but it could not be imported. |
| 304 | +
|
| 305 | + The error was: {e}. |
| 306 | + """) |
| 307 | + |
| 308 | + else: |
| 309 | + _actual_module = sys.modules[__name__] |
| 310 | + |
| 311 | + class SelfWrapper(ModuleType): |
| 312 | + def __getattr__(self, name): |
| 313 | + if name in _pytz_functions: |
| 314 | + raise ImportError( |
| 315 | + f"{name!r} requires pytz (https://pypi.org/project/pytz/), but it could not be imported." |
| 316 | + ) |
| 317 | + else: |
| 318 | + return getattr(_actual_module, name) |
| 319 | + |
| 320 | + sys.modules[__name__] = SelfWrapper(__name__) |
0 commit comments