Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/icalendar/timezone/tzid.py: 70%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

80 statements  

1"""This module identifies timezones. 

2 

3Normally, timezones have ids. 

4This is a way to access the ids if you have a 

5datetime.tzinfo object. 

6""" 

7 

8from __future__ import annotations 

9 

10from collections import defaultdict 

11from datetime import date, time, timezone 

12from pathlib import Path 

13from typing import TYPE_CHECKING 

14 

15from dateutil.tz import tz 

16 

17from icalendar.timezone import equivalent_timezone_ids_result 

18from icalendar.timezone.windows_to_olson import WINDOWS_TO_OLSON 

19from icalendar.tools import is_date 

20 

21try: 

22 from dateutil.tz.win import tzwin as _tzwin 

23except ImportError: 

24 _tzwin = None # not on Windows 

25 

26if TYPE_CHECKING: 

27 from datetime import datetime, tzinfo 

28 

29DATEUTIL_UTC = tz.gettz("UTC") 

30DATEUTIL_UTC_PATH: str | None = getattr(DATEUTIL_UTC, "_filename", None) 

31DATEUTIL_ZONEINFO_PATH = ( 

32 None if DATEUTIL_UTC_PATH is None else Path(DATEUTIL_UTC_PATH).parent 

33) 

34 

35 

36def tzids_from_tzinfo(tzinfo: tzinfo | None) -> tuple[str]: 

37 """Get several timezone ids if we can identify the timezone. 

38 

39 >>> import zoneinfo 

40 >>> from icalendar.timezone.tzid import tzids_from_tzinfo 

41 >>> tzids_from_tzinfo(zoneinfo.ZoneInfo("Arctic/Longyearbyen")) 

42 ('Arctic/Longyearbyen', 'Atlantic/Jan_Mayen', 'Europe/Berlin', 'Europe/Budapest', 'Europe/Copenhagen', 'Europe/Oslo', 'Europe/Stockholm', 'Europe/Vienna') 

43 >>> from dateutil.tz import gettz 

44 >>> tzids_from_tzinfo(gettz("Europe/Berlin")) 

45 ('Europe/Berlin', 'Arctic/Longyearbyen', 'Atlantic/Jan_Mayen', 'Europe/Budapest', 'Europe/Copenhagen', 'Europe/Oslo', 'Europe/Stockholm', 'Europe/Vienna') 

46 

47 """ # The example might need to change if you recreate the lookup tree # noqa: E501 

48 if tzinfo is None: 

49 return () 

50 if isinstance(tzinfo, timezone): 

51 # fixed offset timezone with name 

52 return get_equivalent_tzids(tzinfo.tzname(None)) 

53 if hasattr(tzinfo, "zone"): 

54 return get_equivalent_tzids(tzinfo.zone) # pytz implementation 

55 if hasattr(tzinfo, "key"): 

56 return get_equivalent_tzids(tzinfo.key) # ZoneInfo implementation 

57 if isinstance(tzinfo, tz._tzicalvtz): # noqa: SLF001 

58 return get_equivalent_tzids(tzinfo._tzid) # noqa: SLF001 

59 if isinstance(tzinfo, tz.tzstr): 

60 return get_equivalent_tzids(tzinfo._s) # noqa: SLF001 

61 if _tzwin is not None and isinstance(tzinfo, _tzwin): 

62 olson = WINDOWS_TO_OLSON.get(tzinfo._name) # noqa: SLF001 

63 if olson is not None: 

64 return get_equivalent_tzids(olson) 

65 return get_equivalent_tzids(tzinfo._name) # noqa: SLF001 

66 if hasattr(tzinfo, "_filename"): # dateutil.tz.tzfile # noqa: SIM102 

67 if DATEUTIL_ZONEINFO_PATH is not None: 

68 # tzfile('/usr/share/zoneinfo/Europe/Berlin') 

69 path = tzinfo._filename # noqa: SLF001 

70 if path.startswith(str(DATEUTIL_ZONEINFO_PATH)): 

71 tzid = str(Path(path).relative_to(DATEUTIL_ZONEINFO_PATH)) 

72 return get_equivalent_tzids(tzid) 

73 return get_equivalent_tzids(path) 

74 if isinstance(tzinfo, tz.tzutc): 

75 return get_equivalent_tzids("UTC") 

76 return () 

77 

78 

79def tzid_from_tzinfo(tzinfo: tzinfo | None) -> str | None: 

80 """Retrieve the timezone id from the tzinfo object. 

81 

82 Some timezones are equivalent. 

83 Thus, we might return one ID that is equivalent to others. 

84 """ 

85 tzids = tzids_from_tzinfo(tzinfo) 

86 if "UTC" in tzids: 

87 return "UTC" 

88 if not tzids: 

89 return None 

90 return tzids[0] 

91 

92 

93def tzid_from_dt(dt: datetime) -> str | None: 

94 """Retrieve the timezone id from the datetime object.""" 

95 tzid = tzid_from_tzinfo(dt.tzinfo) 

96 if tzid is None: 

97 return dt.tzname() 

98 return tzid 

99 

100 

101_EQUIVALENT_IDS: dict[str, set[str]] = defaultdict(set) 

102 

103 

104def _add_equivalent_ids(value: tuple | dict | set): 

105 """This adds equivalent ids/ 

106 

107 As soon as one timezone implementation used claims their equivalence, 

108 they are considered equivalent. 

109 Have a look at icalendar.timezone.equivalent_timezone_ids. 

110 """ 

111 if isinstance(value, set): 

112 for tzid in value: 

113 _EQUIVALENT_IDS[tzid].update(value) 

114 elif isinstance(value, tuple): 

115 _add_equivalent_ids(value[1]) 

116 elif isinstance(value, dict): 

117 for value2 in value.values(): 

118 _add_equivalent_ids(value2) 

119 else: 

120 raise TypeError( 

121 f"Expected tuple, dict or set, not {value.__class__.__name__}: {value!r}" 

122 ) 

123 

124 

125_add_equivalent_ids(equivalent_timezone_ids_result.lookup) 

126 

127 

128def get_equivalent_tzids(tzid: str) -> tuple[str]: 

129 """This returns the tzids which are equivalent to this one.""" 

130 ids = _EQUIVALENT_IDS.get(tzid, set()) 

131 return (tzid,) + tuple(sorted(ids - {tzid})) 

132 

133 

134def is_utc(t: datetime | time | date | tzinfo) -> bool: 

135 """Whether this date is in UTC.""" 

136 if is_date(t): 

137 return False 

138 tzid = tzid_from_dt(t) if hasattr(t, "tzinfo") else tzid_from_tzinfo(t) 

139 return tzid == "UTC" 

140 

141 

142__all__ = ["is_utc", "tzid_from_dt", "tzid_from_tzinfo", "tzids_from_tzinfo"]