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, cast 

7 

8if TYPE_CHECKING: 

9 from icalendar.compatibility import TypeGuard, TypeIs 

10 

11 

12def is_date(dt: date | datetime) -> bool: 

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

14 

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

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

17 

18 Parameters: 

19 dt: The date or datetime object to check. 

20 

21 Returns: 

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

23 ``False`` otherwise. 

24 

25 Example: 

26 .. code-block:: pycon 

27 

28 >>> from datetime import date, datetime 

29 >>> from icalendar.tools import is_date 

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

31 True 

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

33 False 

34 """ 

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

36 

37 

38def is_datetime(dt: date | datetime) -> TypeIs[datetime]: 

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

40 

41 Parameters: 

42 dt: The date or datetime object to check. 

43 

44 Returns: 

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

46 only a ``date``. 

47 

48 Example: 

49 .. code-block:: pycon 

50 

51 >>> from datetime import date, datetime 

52 >>> from icalendar.tools import is_datetime 

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

54 True 

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

56 False 

57 """ 

58 return isinstance(dt, datetime) 

59 

60 

61def to_datetime(dt: date | datetime) -> datetime: 

62 """Convert a date to a datetime. 

63 

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

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

66 

67 Parameters: 

68 dt: The date or datetime to convert. 

69 

70 Returns: 

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

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

73 

74 Example: 

75 .. code-block:: pycon 

76 

77 >>> from datetime import date, datetime 

78 >>> from icalendar.tools import to_datetime 

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

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

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

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

83 """ 

84 if is_date(dt): 

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

86 return cast("datetime", dt) 

87 

88 

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

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

91 

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

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

94 

95 Parameters: 

96 tz: The timezone info object to check. 

97 

98 Returns: 

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

100 attribute), ``False`` otherwise. 

101 """ 

102 return hasattr(tz, "localize") 

103 

104 

105def is_pytz_dt(dt: date | datetime) -> TypeGuard[datetime]: 

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

107 

108 This function checks whether the datetime has a timezone attached 

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

110 

111 Parameters: 

112 dt: The date or datetime object to check. 

113 

114 Returns: 

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

116 ``False`` otherwise. 

117 """ 

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

119 

120 

121def normalize_pytz(dt: date | datetime) -> date | datetime: 

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

123 

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

125 operations to correctly adjust the timezone offset, especially around 

126 daylight saving time transitions. 

127 

128 Parameters: 

129 dt: The date or datetime to normalize. 

130 

131 Returns: 

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

133 """ 

134 if is_pytz_dt(dt): 

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

136 return dt 

137 

138 

139__all__ = [ 

140 "is_date", 

141 "is_datetime", 

142 "is_pytz", 

143 "is_pytz_dt", 

144 "normalize_pytz", 

145 "to_datetime", 

146]