Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/icalendar/prop/dt/datetime.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

74 statements  

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

2 

3from datetime import datetime 

4from typing import Any, ClassVar 

5 

6from icalendar.compatibility import Self 

7from icalendar.error import JCalParsingError 

8from icalendar.parser import Parameters 

9from icalendar.timezone import tzp 

10from icalendar.timezone.tzid import is_utc 

11 

12from .base import TimeBase 

13 

14 

15class vDatetime(TimeBase): 

16 """Date-Time 

17 

18 Value Name: 

19 DATE-TIME 

20 

21 Purpose: 

22 This value type is used to identify values that specify a 

23 precise calendar date and time of day. The format is based on 

24 the ISO.8601.2004 complete representation. 

25 

26 Format Definition: 

27 This value type is defined by the following notation: 

28 

29 .. code-block:: text 

30 

31 date-time = date "T" time 

32 

33 date = date-value 

34 date-value = date-fullyear date-month date-mday 

35 date-fullyear = 4DIGIT 

36 date-month = 2DIGIT ;01-12 

37 date-mday = 2DIGIT ;01-28, 01-29, 01-30, 01-31 

38 ;based on month/year 

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

40 time-hour = 2DIGIT ;00-23 

41 time-minute = 2DIGIT ;00-59 

42 time-second = 2DIGIT ;00-60 

43 time-utc = "Z" 

44 

45 The following is the representation of the date-time format. 

46 

47 .. code-block:: text 

48 

49 YYYYMMDDTHHMMSS 

50 

51 Description: 

52 vDatetime is timezone aware and uses a timezone library. 

53 When a vDatetime object is created from an 

54 ical string, you can pass a valid timezone identifier. When a 

55 vDatetime object is created from a Python :py:mod:`datetime` object, it uses the 

56 tzinfo component, if present. Otherwise a timezone-naive object is 

57 created. Be aware that there are certain limitations with timezone naive 

58 DATE-TIME components in the icalendar standard. 

59 

60 Example: 

61 The following represents March 2, 2021 at 10:15 AM with local time: 

62 

63 .. code-block:: pycon 

64 

65 >>> from icalendar import vDatetime 

66 >>> datetime = vDatetime.from_ical("20210302T101500") 

67 >>> datetime.tzname() 

68 >>> datetime.year 

69 2021 

70 >>> datetime.minute 

71 15 

72 

73 The following represents March 2, 2021 at 10:15 AM in New York: 

74 

75 .. code-block:: pycon 

76 

77 >>> datetime = vDatetime.from_ical("20210302T101500", 'America/New_York') 

78 >>> datetime.tzname() 

79 'EST' 

80 

81 The following represents March 2, 2021 at 10:15 AM in Berlin: 

82 

83 .. code-block:: pycon 

84 

85 >>> from zoneinfo import ZoneInfo 

86 >>> timezone = ZoneInfo("Europe/Berlin") 

87 >>> vDatetime.from_ical("20210302T101500", timezone) 

88 datetime.datetime(2021, 3, 2, 10, 15, tzinfo=ZoneInfo(key='Europe/Berlin')) 

89 """ 

90 

91 default_value: ClassVar[str] = "DATE-TIME" 

92 params: Parameters 

93 

94 def __init__(self, dt, /, params: dict[str, Any] | None = None): 

95 self.dt = dt 

96 self.params = Parameters(params) 

97 self.params.update_tzid_from(dt) 

98 

99 def to_ical(self): 

100 dt = self.dt 

101 

102 s = ( 

103 f"{dt.year:04}{dt.month:02}{dt.day:02}" 

104 f"T{dt.hour:02}{dt.minute:02}{dt.second:02}" 

105 ) 

106 if self.is_utc(): 

107 s += "Z" 

108 return s.encode("utf-8") 

109 

110 @staticmethod 

111 def from_ical(ical, timezone=None): 

112 """Create a datetime from the RFC string.""" 

113 tzinfo = None 

114 if isinstance(timezone, str): 

115 tzinfo = tzp.timezone(timezone) 

116 elif timezone is not None: 

117 tzinfo = timezone 

118 

119 try: 

120 timetuple = ( 

121 int(ical[:4]), # year 

122 int(ical[4:6]), # month 

123 int(ical[6:8]), # day 

124 int(ical[9:11]), # hour 

125 int(ical[11:13]), # minute 

126 int(ical[13:15]), # second 

127 ) 

128 if tzinfo: 

129 return tzp.localize(datetime(*timetuple), tzinfo) 

130 if not ical[15:]: 

131 return datetime(*timetuple) 

132 if ical[15:16] == "Z": 

133 return tzp.localize_utc(datetime(*timetuple)) 

134 except Exception as e: 

135 raise ValueError(f"Wrong datetime format: {ical}") from e 

136 raise ValueError(f"Wrong datetime format: {ical}") 

137 

138 @classmethod 

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

140 """Examples of vDatetime.""" 

141 return [cls(datetime(2025, 11, 10, 16, 52))] 

142 

143 from icalendar.param import VALUE 

144 

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

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

147 value = self.dt.strftime("%Y-%m-%dT%H:%M:%S") 

148 if self.is_utc(): 

149 value += "Z" 

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

151 

152 def is_utc(self) -> bool: 

153 """Whether this datetime is UTC.""" 

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

155 

156 @classmethod 

157 def parse_jcal_value(cls, jcal: str) -> datetime: 

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

159 

160 Raises: 

161 ~error.JCalParsingError: If it can't parse a date-time value. 

162 """ 

163 JCalParsingError.validate_value_type(jcal, str, cls) 

164 utc = jcal.endswith("Z") 

165 if utc: 

166 jcal = jcal[:-1] 

167 try: 

168 dt = datetime.strptime(jcal, "%Y-%m-%dT%H:%M:%S") 

169 except ValueError as e: 

170 raise JCalParsingError("Cannot parse date-time.", cls, value=jcal) from e 

171 if utc: 

172 return tzp.localize_utc(dt) 

173 return dt 

174 

175 @classmethod 

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

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

178 

179 Parameters: 

180 jcal_property: The jCal property to parse. 

181 

182 Raises: 

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

184 """ 

185 JCalParsingError.validate_property(jcal_property, cls) 

186 params = Parameters.from_jcal_property(jcal_property) 

187 with JCalParsingError.reraise_with_path_added(3): 

188 dt = cls.parse_jcal_value(jcal_property[3]) 

189 if params.tzid: 

190 dt = tzp.localize(dt, params.tzid) 

191 return cls( 

192 dt, 

193 params=params, 

194 ) 

195 

196 

197__all__ = ["vDatetime"]