Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/icalendar/tools.py: 91%

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

23 statements  

1"""Utility functions for icalendar.""" 

2 

3from __future__ import annotations 

4 

5from datetime import date, datetime, tzinfo 

6from typing import TYPE_CHECKING, Union, cast 

7 

8 

9if TYPE_CHECKING: 

10 from icalendar.compatibility import TypeGuard, TypeIs 

11 

12 

13def is_date(dt: Union[date, datetime]) -> bool: 

14 """Check if a value is a date but not a datetime. 

15 

16 This function distinguishes between ``date`` and ``datetime`` objects, 

17 returning ``True`` only for pure ``date`` instances. 

18 

19 Args: 

20 dt: The date or datetime object to check. 

21 

22 Returns: 

23 ``True`` if the value is a ``date`` but not a ``datetime``, 

24 ``False`` otherwise. 

25 

26 Example: 

27 .. code-block:: pycon 

28 

29 >>> from datetime import date, datetime 

30 >>> from icalendar.tools import is_date 

31 >>> is_date(date(2024, 1, 15)) 

32 True 

33 >>> is_date(datetime(2024, 1, 15, 10, 30)) 

34 False 

35 """ 

36 return isinstance(dt, date) and not isinstance(dt, datetime) 

37 

38 

39def is_datetime(dt: Union[date, datetime]) -> TypeIs[datetime]: 

40 """Check if a value is a datetime. 

41 

42 Args: 

43 dt: The date or datetime object to check. 

44 

45 Returns: 

46 ``True`` if the value is a ``datetime``, ``False`` if it is 

47 only a ``date``. 

48 

49 Example: 

50 .. code-block:: pycon 

51 

52 >>> from datetime import date, datetime 

53 >>> from icalendar.tools import is_datetime 

54 >>> is_datetime(datetime(2024, 1, 15, 10, 30)) 

55 True 

56 >>> is_datetime(date(2024, 1, 15)) 

57 False 

58 """ 

59 return isinstance(dt, datetime) 

60 

61 

62def to_datetime(dt: Union[date, datetime]) -> datetime: 

63 """Convert a date to a datetime. 

64 

65 If the input is already a ``datetime``, it is returned unchanged. 

66 If the input is a ``date``, it is converted to a ``datetime`` at midnight. 

67 

68 Args: 

69 dt: The date or datetime to convert. 

70 

71 Returns: 

72 A ``datetime`` object. If the input was a ``date``, the time 

73 component will be set to midnight (00:00:00). 

74 

75 Example: 

76 .. code-block:: pycon 

77 

78 >>> from datetime import date, datetime 

79 >>> from icalendar.tools import to_datetime 

80 >>> to_datetime(date(2024, 1, 15)) 

81 datetime.datetime(2024, 1, 15, 0, 0) 

82 >>> to_datetime(datetime(2024, 1, 15, 10, 30)) 

83 datetime.datetime(2024, 1, 15, 10, 30) 

84 """ 

85 if is_date(dt): 

86 return datetime(dt.year, dt.month, dt.day) # noqa: DTZ001 

87 return cast("datetime", dt) 

88 

89 

90def is_pytz(tz: tzinfo) -> bool: 

91 """Check if a timezone is a pytz timezone. 

92 

93 pytz timezones require special handling with ``localize()`` and 

94 ``normalize()`` methods for correct timezone calculations. 

95 

96 Args: 

97 tz: The timezone info object to check. 

98 

99 Returns: 

100 ``True`` if the timezone is a pytz timezone (has a ``localize`` 

101 attribute), ``False`` otherwise. 

102 """ 

103 return hasattr(tz, "localize") 

104 

105 

106def is_pytz_dt(dt: Union[date, datetime]) -> TypeGuard[datetime]: 

107 """Check if a datetime uses a pytz timezone. 

108 

109 This function checks whether the datetime has a timezone attached 

110 and whether that timezone is a pytz timezone requiring special handling. 

111 

112 Args: 

113 dt: The date or datetime object to check. 

114 

115 Returns: 

116 ``True`` if the value is a ``datetime`` with a pytz timezone, 

117 ``False`` otherwise. 

118 """ 

119 return is_datetime(dt) and (tzinfo := dt.tzinfo) is not None and is_pytz(tzinfo) 

120 

121 

122def normalize_pytz(dt: Union[date, datetime]) -> Union[date, datetime]: 

123 """Normalize a datetime after calculations when using pytz. 

124 

125 pytz requires the ``normalize()`` function to be called after arithmetic 

126 operations to correctly adjust the timezone offset, especially around 

127 daylight saving time transitions. 

128 

129 Args: 

130 dt: The date or datetime to normalize. 

131 

132 Returns: 

133 The normalized datetime if it uses pytz, otherwise the input unchanged. 

134 """ 

135 if is_pytz_dt(dt): 

136 return dt.tzinfo.normalize(dt) # type: ignore[attr-defined] 

137 return dt 

138 

139 

140__all__ = [ 

141 "is_date", 

142 "is_datetime", 

143 "is_pytz", 

144 "is_pytz_dt", 

145 "normalize_pytz", 

146 "to_datetime", 

147]