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

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

62 statements  

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

2 

3import re 

4from datetime import datetime, time, timezone 

5from typing import Any, ClassVar 

6 

7from icalendar.compatibility import Self 

8from icalendar.error import JCalParsingError 

9from icalendar.parser import Parameters 

10from icalendar.timezone.tzid import is_utc 

11 

12from .base import TimeBase 

13 

14TIME_JCAL_REGEX = re.compile( 

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

16) 

17 

18 

19class vTime(TimeBase): 

20 """Time 

21 

22 Value Name: 

23 TIME 

24 

25 Purpose: 

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

27 time of day. 

28 

29 Format Definition: 

30 This value type is defined by the following notation: 

31 

32 .. code-block:: text 

33 

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

35 

36 time-hour = 2DIGIT ;00-23 

37 time-minute = 2DIGIT ;00-59 

38 time-second = 2DIGIT ;00-60 

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

40 

41 time-utc = "Z" 

42 

43 Description: 

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

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

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

47 vText) is defined for this value type. 

48 

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

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

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

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

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

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

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

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

57 

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

59 type expresses time values in three forms: 

60 

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

62 the following is not valid for a time value: 

63 

64 .. code-block:: text 

65 

66 230000-0800 ;Invalid time format 

67 

68 **FORM #1 LOCAL TIME** 

69 

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

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

72 example, 11:00 PM: 

73 

74 .. code-block:: text 

75 

76 230000 

77 

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

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

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

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

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

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

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

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

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

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

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

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

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

91 

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

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

94 time zone reference MUST be specified. 

95 

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

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

98 existence of "VTIMEZONE" calendar components in the iCalendar 

99 object. 

100 

101 **FORM #2: UTC TIME** 

102 

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

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

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

106 

107 .. code-block:: text 

108 

109 070000Z 

110 

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

112 properties whose time values are specified in UTC. 

113 

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

115 

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

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

118 the appropriate time zone definition. 

119 

120 Example: 

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

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

123 

124 .. code-block:: text 

125 

126 083000 

127 133000Z 

128 TZID=America/New_York:083000 

129 """ 

130 

131 default_value: ClassVar[str] = "TIME" 

132 params: Parameters 

133 

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

135 if len(args) == 1: 

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

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

138 self.dt = args[0] 

139 else: 

140 self.dt = time(*args) 

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

142 self.params.update_tzid_from(self.dt) 

143 

144 def to_ical(self): 

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

146 if self.is_utc(): 

147 value += "Z" 

148 return value 

149 

150 def is_utc(self) -> bool: 

151 """Whether this time is UTC.""" 

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

153 

154 @staticmethod 

155 def from_ical(ical): 

156 # TODO: timezone support 

157 try: 

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

159 return time(*timetuple) 

160 except Exception as e: 

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

162 

163 @classmethod 

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

165 """Examples of vTime.""" 

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

167 

168 from icalendar.param import VALUE 

169 

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

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

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

173 if self.is_utc(): 

174 value += "Z" 

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

176 

177 @classmethod 

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

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

180 

181 Raises: 

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

183 """ 

184 JCalParsingError.validate_value_type(jcal, str, cls) 

185 match = TIME_JCAL_REGEX.match(jcal) 

186 if match is None: 

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

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

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

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

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

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

193 

194 @classmethod 

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

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

197 

198 Parameters: 

199 jcal_property: The jCal property to parse. 

200 

201 Raises: 

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

203 """ 

204 JCalParsingError.validate_property(jcal_property, cls) 

205 with JCalParsingError.reraise_with_path_added(3): 

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

207 return cls( 

208 value, 

209 params=Parameters.from_jcal_property(jcal_property), 

210 ) 

211 

212 

213__all__ = ["vTime"]