Coverage for src/kwai/core/domain/value_objects/timestamp.py: 80%

84 statements  

« prev     ^ index     » next       coverage.py v7.6.10, created at 2024-01-01 00:00 +0000

1"""Module that defines a value object for a local timestamp.""" 

2 

3from dataclasses import dataclass 

4from datetime import date, datetime, time, timedelta, timezone 

5 

6 

7@dataclass(frozen=True) 

8class Timestamp: 

9 """A value object for a timestamp. 

10 

11 The datetime should always be in UTC. 

12 """ 

13 

14 timestamp: datetime | None = None 

15 

16 @property 

17 def empty(self): 

18 """Return True when the timestamp is unknown.""" 

19 return self.timestamp is None 

20 

21 @property 

22 def is_past(self) -> bool: 

23 """Return True when the timestamp in the past.""" 

24 if self.timestamp is None: 

25 raise ValueError("Empty timestamp") 

26 

27 return self.timestamp < Timestamp.create_now().timestamp 

28 

29 @property 

30 def year(self) -> int: 

31 """Return the year.""" 

32 if self.timestamp is None: 

33 raise ValueError("Empty timestamp") 

34 return self.timestamp.year 

35 

36 @property 

37 def month(self) -> int: 

38 """Return the month.""" 

39 if self.timestamp is None: 

40 raise ValueError("Empty timestamp") 

41 return self.timestamp.month 

42 

43 @property 

44 def day(self) -> int: 

45 """Return the day.""" 

46 if self.timestamp is None: 

47 raise ValueError("Empty timestamp") 

48 return self.timestamp.day 

49 

50 @property 

51 def hours(self) -> int: 

52 """Return the hours.""" 

53 if self.timestamp is None: 

54 raise ValueError("Empty timestamp") 

55 return self.timestamp.hour 

56 

57 @property 

58 def minutes(self) -> int: 

59 """Return the minutes.""" 

60 if self.timestamp is None: 

61 raise ValueError("Empty timestamp") 

62 return self.timestamp.minute 

63 

64 @property 

65 def seconds(self) -> int: 

66 """Return the seconds.""" 

67 if self.timestamp is None: 

68 raise ValueError("Empty timestamp") 

69 return self.timestamp.second 

70 

71 @property 

72 def date(self) -> date: 

73 """Return the date.""" 

74 if self.timestamp is None: 

75 raise ValueError("Empty timestamp") 

76 return self.timestamp.date() 

77 

78 @property 

79 def posix(self) -> int: 

80 """Return the POSIX timestamp.""" 

81 if self.timestamp is None: 

82 return 0 

83 return round(self.timestamp.timestamp()) 

84 

85 @property 

86 def time(self) -> time: 

87 """Return the date.""" 

88 if self.timestamp is None: 

89 raise ValueError("Empty timestamp") 

90 return self.timestamp.time() 

91 

92 def __str__(self) -> str: 

93 """Return a string representation. 

94 

95 Returns: 

96 A formatted timestamp in format YYYY-MM-DD HH:mm:ss. 

97 An empty string will be returned, when no timestamp is available. 

98 """ 

99 if self.timestamp is None: 

100 return "" 

101 

102 return self.timestamp.strftime("%Y-%m-%d %H:%M:%S") 

103 

104 def add_delta(self, **kwargs) -> "Timestamp": 

105 """Create a new timestamp by adding the delta to the timestamp. 

106 

107 Returns: 

108 Timestamp: A new timestamp with the delta. 

109 """ 

110 if self.timestamp is None: 

111 raise ValueError("Empty timestamp") 

112 return Timestamp(self.timestamp + timedelta(**kwargs)) 

113 

114 @classmethod 

115 def create_with_delta(cls, **kwargs): 

116 """Create a current local timestamp and applies the delta.""" 

117 return cls.create_now().add_delta(**kwargs) 

118 

119 @classmethod 

120 def create_now(cls): 

121 """Create a timestamp with the current UTC time.""" 

122 return Timestamp(timestamp=datetime.now(timezone.utc)) 

123 

124 @classmethod 

125 def create_from_string( 

126 cls, date_time: str | None = None, date_format: str = "%Y-%m-%d %H:%M:%S" 

127 ) -> "Timestamp": 

128 """Create a timestamp from a string. 

129 

130 Args: 

131 date_time: The string to convert to a timestamp. None or an empty string 

132 will result in an "empty" local timestamp. 

133 date_format: The format used in the string. 

134 """ 

135 if date_time is None: 

136 return Timestamp() 

137 if len(date_time) == 0: 

138 return Timestamp() 

139 return Timestamp( 

140 datetime.strptime(date_time, date_format).replace(tzinfo=timezone.utc) 

141 ) 

142 

143 @classmethod 

144 def create_utc(cls, timestamp: datetime | None): 

145 """Create a timestamp from a datetime and make it UTC. 

146 

147 When None is passed, an empty timestamp will be returned. 

148 """ 

149 if timestamp is None: 

150 return Timestamp() 

151 

152 return Timestamp(timestamp=timestamp.replace(tzinfo=timezone.utc))