Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/icalendar/prop/dt/time.py: 69%

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

77 statements  

1"""TIME property type from :rfc:`5545`.""" 

2 

3import re 

4from datetime import datetime, time, timezone, tzinfo 

5from typing import Any, ClassVar 

6 

7from icalendar.compatibility import Self 

8from icalendar.error import JCalParsingError 

9from icalendar.parser import Parameters 

10from icalendar.timezone import tzp 

11from icalendar.timezone.tzid import is_utc 

12 

13from .base import TimeBase 

14 

15TIME_JCAL_REGEX = re.compile( 

16 r"^(?P<hour>[0-9]{2}):(?P<minute>[0-9]{2}):(?P<second>[0-9]{2})(?P<utc>Z)?$" 

17) 

18 

19 

20class vTime(TimeBase): 

21 """Time 

22 

23 Value Name: 

24 TIME 

25 

26 Purpose: 

27 This value type is used to identify values that contain a 

28 time of day. 

29 

30 Format Definition: 

31 This value type is defined by the following notation: 

32 

33 .. code-block:: text 

34 

35 time = time-hour time-minute time-second [time-utc] 

36 

37 time-hour = 2DIGIT ;00-23 

38 time-minute = 2DIGIT ;00-59 

39 time-second = 2DIGIT ;00-60 

40 ;The "60" value is used to account for positive "leap" seconds. 

41 

42 time-utc = "Z" 

43 

44 Description: 

45 If the property permits, multiple "time" values are 

46 specified by a COMMA-separated list of values. No additional 

47 content value encoding (i.e., BACKSLASH character encoding, see 

48 vText) is defined for this value type. 

49 

50 The "TIME" value type is used to identify values that contain a 

51 time of day. The format is based on the [ISO.8601.2004] complete 

52 representation, basic format for a time of day. The text format 

53 consists of a two-digit, 24-hour of the day (i.e., values 00-23), 

54 two-digit minute in the hour (i.e., values 00-59), and two-digit 

55 seconds in the minute (i.e., values 00-60). The seconds value of 

56 60 MUST only be used to account for positive "leap" seconds. 

57 Fractions of a second are not supported by this format. 

58 

59 In parallel to the "DATE-TIME" definition above, the "TIME" value 

60 type expresses time values in three forms: 

61 

62 The form of time with UTC offset MUST NOT be used. For example, 

63 the following is not valid for a time value: 

64 

65 .. code-block:: ics 

66 

67 230000-0800 ;Invalid time format 

68 

69 **FORM #1 LOCAL TIME** 

70 

71 The local time form is simply a time value that does not contain 

72 the UTC designator nor does it reference a time zone. For 

73 example, 11:00 PM: 

74 

75 .. code-block:: ics 

76 

77 230000 

78 

79 Time values of this type are said to be "floating" and are not 

80 bound to any time zone in particular. They are used to represent 

81 the same hour, minute, and second value regardless of which time 

82 zone is currently being observed. For example, an event can be 

83 defined that indicates that an individual will be busy from 11:00 

84 AM to 1:00 PM every day, no matter which time zone the person is 

85 in. In these cases, a local time can be specified. The recipient 

86 of an iCalendar object with a property value consisting of a local 

87 time, without any relative time zone information, SHOULD interpret 

88 the value as being fixed to whatever time zone the "ATTENDEE" is 

89 in at any given moment. This means that two "Attendees", may 

90 participate in the same event at different UTC times; floating 

91 time SHOULD only be used where that is reasonable behavior. 

92 

93 In most cases, a fixed time is desired. To properly communicate a 

94 fixed time in a property value, either UTC time or local time with 

95 time zone reference MUST be specified. 

96 

97 The use of local time in a TIME value without the "TZID" property 

98 parameter is to be interpreted as floating time, regardless of the 

99 existence of "VTIMEZONE" calendar components in the iCalendar 

100 object. 

101 

102 **FORM #2: UTC TIME** 

103 

104 UTC time, or absolute time, is identified by a LATIN CAPITAL 

105 LETTER Z suffix character, the UTC designator, appended to the 

106 time value. For example, the following represents 07:00 AM UTC: 

107 

108 .. code-block:: ics 

109 

110 070000Z 

111 

112 The "TZID" property parameter MUST NOT be applied to TIME 

113 properties whose time values are specified in UTC. 

114 

115 **FORM #3: LOCAL TIME AND TIME ZONE REFERENCE** 

116 

117 The local time with reference to time zone information form is 

118 identified by the use the "TZID" property parameter to reference 

119 the appropriate time zone definition. 

120 

121 Example: 

122 The following represents 8:30 AM in New York in winter, 

123 five hours behind UTC, in each of the three formats: 

124 

125 .. code-block:: ics 

126 

127 083000 

128 133000Z 

129 TZID=America/New_York:083000 

130 """ 

131 

132 default_value: ClassVar[str] = "TIME" 

133 params: Parameters 

134 

135 def __init__(self, *args, params: dict[str, Any] | None = None): 

136 if len(args) == 1: 

137 if not isinstance(args[0], (time, datetime)): 

138 raise ValueError(f"Expected a datetime.time, got: {args[0]}") 

139 self.dt = args[0] 

140 else: 

141 self.dt = time(*args) 

142 self.params = Parameters(params or {}) 

143 self.params.update_tzid_from(self.dt) 

144 

145 def to_ical(self): 

146 value = self.dt.strftime("%H%M%S") 

147 if self.is_utc(): 

148 value += "Z" 

149 return value 

150 

151 def is_utc(self) -> bool: 

152 """Whether this time is UTC.""" 

153 return self.params.is_utc() or is_utc(self.dt) 

154 

155 @staticmethod 

156 def from_ical(ical: str, timezone: str | None | tzinfo = None) -> time: 

157 """Convert an ical string into a time. 

158 

159 This method supports parsing the three forms of time values defined in :rfc:`5545#section-3.3.12`: 

160 - Local time (floating) 

161 - UTC time 

162 - Local time with time zone reference 

163 

164 Returns: 

165 A :class:`datetime.time` object representing the parsed time, with timezone information if applicable. 

166 

167 Raises: 

168 ValueError: if the provided string cannot be parsed as a time. 

169 """ 

170 tzinfo = None 

171 if isinstance(timezone, str): 

172 tzinfo = tzp.timezone(timezone) 

173 elif timezone is not None: 

174 tzinfo = timezone 

175 

176 try: 

177 if isinstance(ical, bytes): 

178 ical = ical.decode() 

179 utc = ical.endswith("Z") 

180 if utc: 

181 ical = ical[:-1] 

182 timetuple = (int(ical[:2]), int(ical[2:4]), int(ical[4:6])) 

183 if tzinfo: 

184 return tzp.localize(time(*timetuple), tzinfo) 

185 if utc: 

186 return tzp.localize_utc(time(*timetuple)) 

187 return time(*timetuple) 

188 except Exception as e: 

189 raise ValueError(f"Expected time, got: {ical}") from e 

190 

191 @classmethod 

192 def examples(cls) -> list[Self]: 

193 """Examples of vTime.""" 

194 return [cls(time(12, 30))] 

195 

196 from icalendar.param import VALUE 

197 

198 def to_jcal(self, name: str) -> list: 

199 """The jCal representation of this property according to :rfc:`7265`.""" 

200 value = self.dt.strftime("%H:%M:%S") 

201 if self.is_utc(): 

202 value += "Z" 

203 return [name, self.params.to_jcal(exclude_utc=True), self.VALUE.lower(), value] 

204 

205 @classmethod 

206 def parse_jcal_value(cls, jcal: str) -> time: 

207 """Parse a jCal string to a :py:class:`datetime.time`. 

208 

209 Raises: 

210 ~error.JCalParsingError: If it can't parse a time. 

211 """ 

212 JCalParsingError.validate_value_type(jcal, str, cls) 

213 match = TIME_JCAL_REGEX.match(jcal) 

214 if match is None: 

215 raise JCalParsingError("Cannot parse time.", cls, value=jcal) 

216 hour = int(match.group("hour")) 

217 minute = int(match.group("minute")) 

218 second = int(match.group("second")) 

219 utc = bool(match.group("utc")) 

220 return time(hour, minute, second, tzinfo=timezone.utc if utc else None) 

221 

222 @classmethod 

223 def from_jcal(cls, jcal_property: list) -> Self: 

224 """Parse jCal from :rfc:`7265`. 

225 

226 Parameters: 

227 jcal_property: The jCal property to parse. 

228 

229 Raises: 

230 ~error.JCalParsingError: If the provided jCal is invalid. 

231 """ 

232 JCalParsingError.validate_property(jcal_property, cls) 

233 with JCalParsingError.reraise_with_path_added(3): 

234 value = cls.parse_jcal_value(jcal_property[3]) 

235 return cls( 

236 value, 

237 params=Parameters.from_jcal_property(jcal_property), 

238 ) 

239 

240 

241__all__ = ["vTime"]