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

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  

1from __future__ import annotations 

2 

3from typing import TYPE_CHECKING, Optional, Union 

4 

5from icalendar.tools import to_datetime 

6 

7from .windows_to_olson import WINDOWS_TO_OLSON 

8 

9if TYPE_CHECKING: 

10 import datetime 

11 

12 from dateutil.rrule import rrule 

13 

14 from icalendar import prop 

15 from icalendar.cal import Timezone 

16 

17 from .provider import TZProvider 

18 

19DEFAULT_TIMEZONE_PROVIDER = "zoneinfo" 

20 

21 

22class TZP: 

23 """This is the timezone provider proxy. 

24 

25 If you would like to have another timezone implementation, 

26 you can create a new one and pass it to this proxy. 

27 All of icalendar will then use this timezone implementation. 

28 """ 

29 

30 def __init__(self, provider: Union[str, TZProvider] = DEFAULT_TIMEZONE_PROVIDER): 

31 """Create a new timezone implementation proxy.""" 

32 self.use(provider) 

33 

34 def use_pytz(self) -> None: 

35 """Use pytz as the timezone provider.""" 

36 from .pytz import PYTZ 

37 

38 self._use(PYTZ()) 

39 

40 def use_zoneinfo(self) -> None: 

41 """Use zoneinfo as the timezone provider.""" 

42 from .zoneinfo import ZONEINFO 

43 

44 self._use(ZONEINFO()) 

45 

46 def _use(self, provider: TZProvider) -> None: 

47 """Use a timezone implementation.""" 

48 self.__tz_cache = {} 

49 self.__provider = provider 

50 

51 def use(self, provider: Union[str, TZProvider]): 

52 """Switch to a different timezone provider.""" 

53 if isinstance(provider, str): 

54 use_provider = getattr(self, f"use_{provider}", None) 

55 if use_provider is None: 

56 raise ValueError( 

57 f"Unknown provider {provider}. Use 'pytz' or 'zoneinfo'." 

58 ) 

59 use_provider() 

60 else: 

61 self._use(provider) 

62 

63 def use_default(self): 

64 """Use the default timezone provider.""" 

65 self.use(DEFAULT_TIMEZONE_PROVIDER) 

66 

67 def localize_utc(self, dt: datetime.date) -> datetime.datetime: 

68 """Return the datetime in UTC. 

69 

70 If the datetime has no timezone, set UTC as its timezone. 

71 """ 

72 return self.__provider.localize_utc(to_datetime(dt)) 

73 

74 def localize( 

75 self, dt: datetime.date, tz: Union[datetime.tzinfo, str, None] 

76 ) -> datetime.datetime: 

77 """Localize a datetime to a timezone.""" 

78 if isinstance(tz, str): 

79 tz = self.timezone(tz) 

80 if tz is None: 

81 return dt.replace(tzinfo=None) 

82 return self.__provider.localize(to_datetime(dt), tz) 

83 

84 def cache_timezone_component(self, timezone_component: Timezone.Timezone) -> None: 

85 """Cache the timezone that is created from a timezone component 

86 if it is not already known. 

87 

88 This can influence the result from timezone(): Once cached, the 

89 custom timezone is returned from timezone(). 

90 """ 

91 _unclean_id = timezone_component["TZID"] 

92 _id = self.clean_timezone_id(_unclean_id) 

93 if ( 

94 not self.__provider.knows_timezone_id(_id) 

95 and not self.__provider.knows_timezone_id(_unclean_id) 

96 and _id not in self.__tz_cache 

97 ): 

98 self.__tz_cache[_id] = timezone_component.to_tz(self, lookup_tzid=False) 

99 

100 def fix_rrule_until(self, rrule: rrule, ical_rrule: prop.vRecur) -> None: 

101 """Make sure the until value works.""" 

102 self.__provider.fix_rrule_until(rrule, ical_rrule) 

103 

104 def create_timezone(self, timezone_component: Timezone.Timezone) -> datetime.tzinfo: 

105 """Create a timezone from a timezone component. 

106 

107 This component will not be cached. 

108 """ 

109 return self.__provider.create_timezone(timezone_component) 

110 

111 def clean_timezone_id(self, tzid: str) -> str: 

112 """Return a clean version of the timezone id. 

113 

114 Timezone ids can be a bit unclean, starting with a / for example. 

115 Internally, we should use this to identify timezones. 

116 """ 

117 return tzid.strip("/") 

118 

119 def timezone(self, tz_id: str) -> Optional[datetime.tzinfo]: 

120 """Return a timezone with an id or None if we cannot find it.""" 

121 _unclean_id = tz_id 

122 tz_id = self.clean_timezone_id(tz_id) 

123 tz = self.__provider.timezone(tz_id) 

124 if tz is not None: 

125 return tz 

126 if tz_id in WINDOWS_TO_OLSON: 

127 tz = self.__provider.timezone(WINDOWS_TO_OLSON[tz_id]) 

128 return tz or self.__provider.timezone(_unclean_id) or self.__tz_cache.get(tz_id) 

129 

130 def uses_pytz(self) -> bool: 

131 """Whether we use pytz at all.""" 

132 return self.__provider.uses_pytz() 

133 

134 def uses_zoneinfo(self) -> bool: 

135 """Whether we use zoneinfo.""" 

136 return self.__provider.uses_zoneinfo() 

137 

138 @property 

139 def name(self) -> str: 

140 """The name of the timezone component used.""" 

141 return self.__provider.name 

142 

143 def __repr__(self) -> str: 

144 return f"{self.__class__.__name__}({self.name!r})" 

145 

146 

147__all__ = ["TZP"]