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
« 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."""
3from dataclasses import dataclass
4from datetime import date, datetime, time, timedelta, timezone
7@dataclass(frozen=True)
8class Timestamp:
9 """A value object for a timestamp.
11 The datetime should always be in UTC.
12 """
14 timestamp: datetime | None = None
16 @property
17 def empty(self):
18 """Return True when the timestamp is unknown."""
19 return self.timestamp is None
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")
27 return self.timestamp < Timestamp.create_now().timestamp
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
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
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
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
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
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
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()
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())
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()
92 def __str__(self) -> str:
93 """Return a string representation.
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 ""
102 return self.timestamp.strftime("%Y-%m-%d %H:%M:%S")
104 def add_delta(self, **kwargs) -> "Timestamp":
105 """Create a new timestamp by adding the delta to the timestamp.
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))
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)
119 @classmethod
120 def create_now(cls):
121 """Create a timestamp with the current UTC time."""
122 return Timestamp(timestamp=datetime.now(timezone.utc))
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.
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 )
143 @classmethod
144 def create_utc(cls, timestamp: datetime | None):
145 """Create a timestamp from a datetime and make it UTC.
147 When None is passed, an empty timestamp will be returned.
148 """
149 if timestamp is None:
150 return Timestamp()
152 return Timestamp(timestamp=timestamp.replace(tzinfo=timezone.utc))