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

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

70 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, Optional 

14 

15from dateutil.tz import tz 

16 

17from icalendar.timezone import equivalent_timezone_ids_result 

18from icalendar.tools import is_date 

19 

20if TYPE_CHECKING: 

21 from datetime import datetime, tzinfo 

22 

23DATEUTIL_UTC = tz.gettz("UTC") 

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

25DATEUTIL_ZONEINFO_PATH = ( 

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

27) 

28 

29 

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

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

32 

33 >>> import zoneinfo 

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

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

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

37 >>> from dateutil.tz import gettz 

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

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

40 

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

42 if tzinfo is None: 

43 return () 

44 if isinstance(tzinfo, timezone): 

45 # fixed offset timezone with name 

46 return get_equivalent_tzids(tzinfo.tzname(None)) 

47 if hasattr(tzinfo, "zone"): 

48 return get_equivalent_tzids(tzinfo.zone) # pytz implementation 

49 if hasattr(tzinfo, "key"): 

50 return get_equivalent_tzids(tzinfo.key) # ZoneInfo implementation 

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

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

53 if isinstance(tzinfo, tz.tzstr): 

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

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

56 if DATEUTIL_ZONEINFO_PATH is not None: 

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

58 path = tzinfo._filename # noqa: SLF001 

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

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

61 return get_equivalent_tzids(tzid) 

62 return get_equivalent_tzids(path) 

63 if isinstance(tzinfo, tz.tzutc): 

64 return get_equivalent_tzids("UTC") 

65 return () 

66 

67 

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

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

70 

71 Some timezones are equivalent. 

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

73 """ 

74 tzids = tzids_from_tzinfo(tzinfo) 

75 if "UTC" in tzids: 

76 return "UTC" 

77 if not tzids: 

78 return None 

79 return tzids[0] 

80 

81 

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

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

84 tzid = tzid_from_tzinfo(dt.tzinfo) 

85 if tzid is None: 

86 return dt.tzname() 

87 return tzid 

88 

89 

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

91 

92 

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

94 """This adds equivalent ids/ 

95 

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

97 they are considered equivalent. 

98 Have a look at icalendar.timezone.equivalent_timezone_ids. 

99 """ 

100 if isinstance(value, set): 

101 for tzid in value: 

102 _EQUIVALENT_IDS[tzid].update(value) 

103 elif isinstance(value, tuple): 

104 _add_equivalent_ids(value[1]) 

105 elif isinstance(value, dict): 

106 for value2 in value.values(): 

107 _add_equivalent_ids(value2) 

108 else: 

109 raise TypeError( 

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

111 ) 

112 

113 

114_add_equivalent_ids(equivalent_timezone_ids_result.lookup) 

115 

116 

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

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

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

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

121 

122 

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

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

125 if is_date(t): 

126 return False 

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

128 return tzid == "UTC" 

129 

130 

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