Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/pendulum/time.py: 39%

134 statements  

« prev     ^ index     » next       coverage.py v7.3.1, created at 2023-09-30 06:11 +0000

1from __future__ import annotations 

2 

3import datetime 

4 

5from datetime import time 

6from datetime import timedelta 

7from typing import TYPE_CHECKING 

8from typing import Optional 

9from typing import cast 

10from typing import overload 

11 

12import pendulum 

13 

14from pendulum.constants import SECS_PER_HOUR 

15from pendulum.constants import SECS_PER_MIN 

16from pendulum.constants import USECS_PER_SEC 

17from pendulum.duration import AbsoluteDuration 

18from pendulum.duration import Duration 

19from pendulum.mixins.default import FormattableMixin 

20from pendulum.tz.timezone import UTC 

21 

22 

23if TYPE_CHECKING: 

24 from typing_extensions import Literal 

25 from typing_extensions import Self 

26 from typing_extensions import SupportsIndex 

27 

28 from pendulum.tz.timezone import FixedTimezone 

29 from pendulum.tz.timezone import Timezone 

30 

31 

32class Time(FormattableMixin, time): 

33 """ 

34 Represents a time instance as hour, minute, second, microsecond. 

35 """ 

36 

37 @classmethod 

38 def instance( 

39 cls, t: time, tz: str | Timezone | FixedTimezone | datetime.tzinfo | None = UTC 

40 ) -> Self: 

41 tz = t.tzinfo or tz 

42 

43 if tz is not None: 

44 tz = pendulum._safe_timezone(tz) 

45 

46 return cls(t.hour, t.minute, t.second, t.microsecond, tzinfo=tz, fold=t.fold) 

47 

48 # String formatting 

49 def __repr__(self) -> str: 

50 us = "" 

51 if self.microsecond: 

52 us = f", {self.microsecond}" 

53 

54 tzinfo = "" 

55 if self.tzinfo: 

56 tzinfo = f", tzinfo={self.tzinfo!r}" 

57 

58 return ( 

59 f"{self.__class__.__name__}" 

60 f"({self.hour}, {self.minute}, {self.second}{us}{tzinfo})" 

61 ) 

62 

63 # Comparisons 

64 

65 def closest(self, dt1: Time | time, dt2: Time | time) -> Self: 

66 """ 

67 Get the closest time from the instance. 

68 """ 

69 dt1 = self.__class__(dt1.hour, dt1.minute, dt1.second, dt1.microsecond) 

70 dt2 = self.__class__(dt2.hour, dt2.minute, dt2.second, dt2.microsecond) 

71 

72 if self.diff(dt1).in_seconds() < self.diff(dt2).in_seconds(): 

73 return dt1 

74 

75 return dt2 

76 

77 def farthest(self, dt1: Time | time, dt2: Time | time) -> Self: 

78 """ 

79 Get the farthest time from the instance. 

80 """ 

81 dt1 = self.__class__(dt1.hour, dt1.minute, dt1.second, dt1.microsecond) 

82 dt2 = self.__class__(dt2.hour, dt2.minute, dt2.second, dt2.microsecond) 

83 

84 if self.diff(dt1).in_seconds() > self.diff(dt2).in_seconds(): 

85 return dt1 

86 

87 return dt2 

88 

89 # ADDITIONS AND SUBSTRACTIONS 

90 

91 def add( 

92 self, hours: int = 0, minutes: int = 0, seconds: int = 0, microseconds: int = 0 

93 ) -> Time: 

94 """ 

95 Add duration to the instance. 

96 

97 :param hours: The number of hours 

98 :param minutes: The number of minutes 

99 :param seconds: The number of seconds 

100 :param microseconds: The number of microseconds 

101 """ 

102 from pendulum.datetime import DateTime 

103 

104 return ( 

105 DateTime.EPOCH.at(self.hour, self.minute, self.second, self.microsecond) 

106 .add( 

107 hours=hours, minutes=minutes, seconds=seconds, microseconds=microseconds 

108 ) 

109 .time() 

110 ) 

111 

112 def subtract( 

113 self, hours: int = 0, minutes: int = 0, seconds: int = 0, microseconds: int = 0 

114 ) -> Time: 

115 """ 

116 Add duration to the instance. 

117 

118 :param hours: The number of hours 

119 :type hours: int 

120 

121 :param minutes: The number of minutes 

122 :type minutes: int 

123 

124 :param seconds: The number of seconds 

125 :type seconds: int 

126 

127 :param microseconds: The number of microseconds 

128 :type microseconds: int 

129 

130 :rtype: Time 

131 """ 

132 from pendulum.datetime import DateTime 

133 

134 return ( 

135 DateTime.EPOCH.at(self.hour, self.minute, self.second, self.microsecond) 

136 .subtract( 

137 hours=hours, minutes=minutes, seconds=seconds, microseconds=microseconds 

138 ) 

139 .time() 

140 ) 

141 

142 def add_timedelta(self, delta: datetime.timedelta) -> Time: 

143 """ 

144 Add timedelta duration to the instance. 

145 

146 :param delta: The timedelta instance 

147 """ 

148 if delta.days: 

149 raise TypeError("Cannot add timedelta with days to Time.") 

150 

151 return self.add(seconds=delta.seconds, microseconds=delta.microseconds) 

152 

153 def subtract_timedelta(self, delta: datetime.timedelta) -> Time: 

154 """ 

155 Remove timedelta duration from the instance. 

156 

157 :param delta: The timedelta instance 

158 """ 

159 if delta.days: 

160 raise TypeError("Cannot subtract timedelta with days to Time.") 

161 

162 return self.subtract(seconds=delta.seconds, microseconds=delta.microseconds) 

163 

164 def __add__(self, other: datetime.timedelta) -> Time: 

165 if not isinstance(other, timedelta): 

166 return NotImplemented 

167 

168 return self.add_timedelta(other) 

169 

170 @overload 

171 def __sub__(self, other: time) -> pendulum.Duration: 

172 ... 

173 

174 @overload 

175 def __sub__(self, other: datetime.timedelta) -> Time: 

176 ... 

177 

178 def __sub__(self, other: time | datetime.timedelta) -> pendulum.Duration | Time: 

179 if not isinstance(other, (Time, time, timedelta)): 

180 return NotImplemented 

181 

182 if isinstance(other, timedelta): 

183 return self.subtract_timedelta(other) 

184 

185 if isinstance(other, time): 

186 if other.tzinfo is not None: 

187 raise TypeError("Cannot subtract aware times to or from Time.") 

188 

189 other = self.__class__( 

190 other.hour, other.minute, other.second, other.microsecond 

191 ) 

192 

193 return other.diff(self, False) 

194 

195 @overload 

196 def __rsub__(self, other: time) -> pendulum.Duration: 

197 ... 

198 

199 @overload 

200 def __rsub__(self, other: datetime.timedelta) -> Time: 

201 ... 

202 

203 def __rsub__(self, other: time | datetime.timedelta) -> pendulum.Duration | Time: 

204 if not isinstance(other, (Time, time)): 

205 return NotImplemented 

206 

207 if isinstance(other, time): 

208 if other.tzinfo is not None: 

209 raise TypeError("Cannot subtract aware times to or from Time.") 

210 

211 other = self.__class__( 

212 other.hour, other.minute, other.second, other.microsecond 

213 ) 

214 

215 return other.__sub__(self) 

216 

217 # DIFFERENCES 

218 

219 def diff(self, dt: time | None = None, abs: bool = True) -> Duration: 

220 """ 

221 Returns the difference between two Time objects as an Duration. 

222 

223 :param dt: The time to subtract from 

224 :param abs: Whether to return an absolute duration or not 

225 """ 

226 if dt is None: 

227 dt = pendulum.now().time() 

228 else: 

229 dt = self.__class__(dt.hour, dt.minute, dt.second, dt.microsecond) 

230 

231 us1 = ( 

232 self.hour * SECS_PER_HOUR + self.minute * SECS_PER_MIN + self.second 

233 ) * USECS_PER_SEC 

234 

235 us2 = ( 

236 dt.hour * SECS_PER_HOUR + dt.minute * SECS_PER_MIN + dt.second 

237 ) * USECS_PER_SEC 

238 

239 klass = Duration 

240 if abs: 

241 klass = AbsoluteDuration 

242 

243 return klass(microseconds=us2 - us1) 

244 

245 def diff_for_humans( 

246 self, 

247 other: time | None = None, 

248 absolute: bool = False, 

249 locale: str | None = None, 

250 ) -> str: 

251 """ 

252 Get the difference in a human readable format in the current locale. 

253 

254 :param dt: The time to subtract from 

255 :param absolute: removes time difference modifiers ago, after, etc 

256 :param locale: The locale to use for localization 

257 """ 

258 is_now = other is None 

259 

260 if is_now: 

261 other = pendulum.now().time() 

262 

263 diff = self.diff(other) 

264 

265 return pendulum.format_diff(diff, is_now, absolute, locale) 

266 

267 # Compatibility methods 

268 

269 def replace( 

270 self, 

271 hour: SupportsIndex | None = None, 

272 minute: SupportsIndex | None = None, 

273 second: SupportsIndex | None = None, 

274 microsecond: SupportsIndex | None = None, 

275 tzinfo: bool | datetime.tzinfo | Literal[True] | None = True, 

276 fold: int = 0, 

277 ) -> Self: 

278 if tzinfo is True: 

279 tzinfo = self.tzinfo 

280 

281 hour = hour if hour is not None else self.hour 

282 minute = minute if minute is not None else self.minute 

283 second = second if second is not None else self.second 

284 microsecond = microsecond if microsecond is not None else self.microsecond 

285 

286 t = super().replace( 

287 hour, 

288 minute, 

289 second, 

290 microsecond, 

291 tzinfo=cast(Optional[datetime.tzinfo], tzinfo), 

292 fold=fold, 

293 ) 

294 return self.__class__( 

295 t.hour, t.minute, t.second, t.microsecond, tzinfo=t.tzinfo 

296 ) 

297 

298 def __getnewargs__(self) -> tuple[Time]: 

299 return (self,) 

300 

301 def _get_state( 

302 self, protocol: SupportsIndex = 3 

303 ) -> tuple[int, int, int, int, datetime.tzinfo | None]: 

304 tz = self.tzinfo 

305 

306 return self.hour, self.minute, self.second, self.microsecond, tz 

307 

308 def __reduce__( 

309 self, 

310 ) -> tuple[type[Time], tuple[int, int, int, int, datetime.tzinfo | None]]: 

311 return self.__reduce_ex__(2) 

312 

313 def __reduce_ex__( 

314 self, protocol: SupportsIndex 

315 ) -> tuple[type[Time], tuple[int, int, int, int, datetime.tzinfo | None]]: 

316 return self.__class__, self._get_state(protocol) 

317 

318 

319Time.min = Time(0, 0, 0) 

320Time.max = Time(23, 59, 59, 999999) 

321Time.resolution = Duration(microseconds=1)