Coverage for src/sensai/util/time.py: 34%

41 statements  

« prev     ^ index     » next       coverage.py v7.6.1, created at 2024-08-13 22:17 +0000

1from datetime import time 

2from typing import TYPE_CHECKING 

3 

4if TYPE_CHECKING: 

5 import pandas as pd 

6 

7 

8def ts_next_month(ts: "pd.Timestamp") -> "pd.Timestamp": 

9 m = ts.month 

10 if m == 12: 

11 return ts.replace(year=ts.year+1, month=1) 

12 else: 

13 return ts.replace(month=m+1) 

14 

15 

16def time_of_day(ts: "pd.Timestamp") -> float: 

17 """ 

18 :param ts: the timestamp 

19 :return: the time of day as a floating point number in [0, 24) 

20 """ 

21 return ts.hour + ts.minute / 60 

22 

23 

24class TimeInterval: 

25 def __init__(self, start: "pd.Timestamp", end: "pd.Timestamp"): 

26 self.start = start 

27 self.end = end 

28 

29 def contains(self, t: "pd.Timestamp"): 

30 return self.start <= t <= self.end 

31 

32 def contains_time(self, t: time): 

33 """ 

34 :param t: a time of day 

35 :return: True iff the time interval contains the given time of day at least once, False otherwise 

36 """ 

37 if (self.end - self.start).total_seconds() >= (60 * 60 * 24): 

38 return True 

39 return self.contains(self.start.replace(hour=t.hour, minute=t.minute, second=t.second, microsecond=t.microsecond)) or \ 

40 self.contains(self.end.replace(hour=t.hour, minute=t.minute, second=t.second, microsecond=t.microsecond)) 

41 

42 def overlaps_with(self, other: "TimeInterval") -> bool: 

43 other_ends_before = other.end <= self.start 

44 other_starts_after = other.start >= self.end 

45 return not (other_ends_before or other_starts_after) 

46 

47 def intersection(self, other: "TimeInterval") -> "TimeInterval": 

48 return TimeInterval(max(self.start, other.start), min(self.end, other.end)) 

49 

50 def time_delta(self) -> "pd.Timedelta": 

51 return self.end - self.start 

52 

53 def mid_timestamp(self) -> "pd.Timestamp": 

54 midTime: pd.Timestamp = self.start + 0.5 * self.time_delta() 

55 return midTime 

56 

57 

58def format_duration(seconds: float): 

59 if seconds < 60: 

60 return f"{seconds:.1f} seconds" 

61 elif seconds < 3600: 

62 minutes, secs = divmod(seconds, 60) 

63 return f"{int(minutes)} minutes, {secs:.1f} seconds" 

64 else: 

65 hours, remainder = divmod(seconds, 3600) 

66 minutes, secs = divmod(remainder, 60) 

67 return f"{int(hours)} hours, {int(minutes)} minutes, {secs:.1f} seconds"