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

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

61 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 pathlib import Path 

12from typing import TYPE_CHECKING, Optional 

13 

14from dateutil.tz import tz 

15 

16from icalendar.timezone import equivalent_timezone_ids_result 

17 

18if TYPE_CHECKING: 

19 from datetime import datetime, tzinfo 

20 

21DATEUTIL_UTC = tz.gettz("UTC") 

22DATEUTIL_UTC_PATH: Optional[str] = getattr(DATEUTIL_UTC, "_filename", None) 

23DATEUTIL_ZONEINFO_PATH = ( 

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

25) 

26 

27 

28def tzids_from_tzinfo(tzinfo: Optional[tzinfo]) -> tuple[str]: 

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

30 

31 >>> import zoneinfo 

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

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

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

35 >>> from dateutil.tz import gettz 

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

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

38 

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

40 if tzinfo is None: 

41 return () 

42 if hasattr(tzinfo, "zone"): 

43 return get_equivalent_tzids(tzinfo.zone) # pytz implementation 

44 if hasattr(tzinfo, "key"): 

45 return get_equivalent_tzids(tzinfo.key) # ZoneInfo implementation 

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

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

48 if isinstance(tzinfo, tz.tzstr): 

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

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

51 if DATEUTIL_ZONEINFO_PATH is not None: 

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

53 path = tzinfo._filename # noqa: SLF001 

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

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

56 return get_equivalent_tzids(tzid) 

57 return get_equivalent_tzids(path) 

58 if isinstance(tzinfo, tz.tzutc): 

59 return get_equivalent_tzids("UTC") 

60 return () 

61 

62 

63def tzid_from_tzinfo(tzinfo: Optional[tzinfo]) -> Optional[str]: 

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

65 

66 Some timezones are equivalent. 

67 Thus, we might return one ID that is equivelant to others. 

68 """ 

69 tzids = tzids_from_tzinfo(tzinfo) 

70 if "UTC" in tzids: 

71 return "UTC" 

72 if not tzids: 

73 return None 

74 return tzids[0] 

75 

76 

77def tzid_from_dt(dt: datetime) -> Optional[str]: 

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

79 tzid = tzid_from_tzinfo(dt.tzinfo) 

80 if tzid is None: 

81 return dt.tzname() 

82 return tzid 

83 

84 

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

86 

87 

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

89 """This adds equivalent ids/ 

90 

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

92 they are considered equivalent. 

93 Have a look at icalendar.timezone.equivalent_timezone_ids. 

94 """ 

95 if isinstance(value, set): 

96 for tzid in value: 

97 _EQUIVALENT_IDS[tzid].update(value) 

98 elif isinstance(value, tuple): 

99 _add_equivalent_ids(value[1]) 

100 elif isinstance(value, dict): 

101 for value2 in value.values(): 

102 _add_equivalent_ids(value2) 

103 else: 

104 raise TypeError( 

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

106 ) 

107 

108 

109_add_equivalent_ids(equivalent_timezone_ids_result.lookup) 

110 

111 

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

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

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

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

116 

117 

118__all__ = ["tzid_from_dt", "tzid_from_tzinfo", "tzids_from_tzinfo"]