raphtory_api/core/utils/
time.rs1use crate::core::storage::timeindex::{AsTime, EventTime};
2#[cfg(feature = "python")]
3use crate::python::error::adapt_err_value;
4use chrono::{DateTime, NaiveDate, NaiveDateTime, ParseError, TimeZone};
5#[cfg(feature = "python")]
6use pyo3::PyErr;
7use std::{convert::Infallible, num::ParseIntError};
8
9#[derive(thiserror::Error, Debug, Clone, PartialEq)]
10pub enum ParseTimeError {
11 #[error("The interval string doesn't contain a complete number of number-unit pairs.")]
12 InvalidPairs,
13 #[error(
14 "One of the tokens in the interval string supposed to be a number couldn't be parsed."
15 )]
16 ParseInt {
17 #[from]
18 source: ParseIntError,
19 },
20 #[error("'{0}' is not a valid unit. Valid units are year(s), month(s), week(s), day(s), hour(s), minute(s), second(s) and millisecond(s).")]
21 InvalidUnit(String),
22 #[error("'{0}' is not a valid unit. Valid units are year(s), month(s), week(s), day(s), hour(s), minute(s), second(s), millisecond(s), and unaligned.")]
23 InvalidAlignmentUnit(String),
24 #[error(transparent)]
25 ParseError(#[from] ParseError),
26 #[error("Negative interval is not supported.")]
27 NegativeInt,
28 #[error("0 size step is not supported.")]
29 ZeroSizeStep,
30 #[error("'{0}' is not a valid datetime. Valid formats are RFC3339, RFC2822, %Y-%m-%d, %Y-%m-%dT%H:%M:%S%.3f, %Y-%m-%dT%H:%M:%S%, %Y-%m-%d %H:%M:%S%.3f and %Y-%m-%d %H:%M:%S%")]
31 InvalidDateTimeString(String),
32}
33
34impl From<Infallible> for ParseTimeError {
35 fn from(value: Infallible) -> Self {
36 match value {}
37 }
38}
39
40#[cfg(feature = "python")]
41impl From<ParseTimeError> for PyErr {
42 fn from(value: ParseTimeError) -> Self {
43 adapt_err_value(&value)
44 }
45}
46
47pub trait IntoTime {
48 fn into_time(self) -> EventTime;
49}
50
51impl IntoTime for i64 {
52 fn into_time(self) -> EventTime {
53 EventTime::from(self)
54 }
55}
56
57impl<Tz: TimeZone> IntoTime for DateTime<Tz> {
58 fn into_time(self) -> EventTime {
59 EventTime::from(self.timestamp_millis())
60 }
61}
62
63impl IntoTime for NaiveDateTime {
64 fn into_time(self) -> EventTime {
65 EventTime::from(self.and_utc().timestamp_millis())
66 }
67}
68
69impl IntoTime for EventTime {
70 fn into_time(self) -> EventTime {
71 self
72 }
73}
74
75impl<T: IntoTime> IntoTime for (T, usize) {
76 fn into_time(self) -> EventTime {
77 self.0.into_time().set_event_id(self.1)
78 }
79}
80
81pub trait TryIntoTime {
82 fn try_into_time(self) -> Result<EventTime, ParseTimeError>;
83}
84
85impl<T: IntoTime> TryIntoTime for T {
86 fn try_into_time(self) -> Result<EventTime, ParseTimeError> {
87 Ok(self.into_time())
88 }
89}
90
91impl TryIntoTime for &str {
92 fn try_into_time(self) -> Result<EventTime, ParseTimeError> {
95 let rfc_result = DateTime::parse_from_rfc3339(self);
96 if let Ok(datetime) = rfc_result {
97 return Ok(EventTime::from(datetime.timestamp_millis()));
98 }
99
100 let result = DateTime::parse_from_rfc2822(self);
101 if let Ok(datetime) = result {
102 return Ok(EventTime::from(datetime.timestamp_millis()));
103 }
104
105 let result = NaiveDate::parse_from_str(self, "%Y-%m-%d");
106 if let Ok(date) = result {
107 let timestamp = date
108 .and_hms_opt(00, 00, 00)
109 .unwrap()
110 .and_utc()
111 .timestamp_millis();
112 return Ok(EventTime::from(timestamp));
113 }
114
115 let result = NaiveDateTime::parse_from_str(self, "%Y-%m-%dT%H:%M:%S%.3f");
116 if let Ok(datetime) = result {
117 return Ok(EventTime::from(datetime.and_utc().timestamp_millis()));
118 }
119
120 let result = NaiveDateTime::parse_from_str(self, "%Y-%m-%dT%H:%M:%S%");
121 if let Ok(datetime) = result {
122 return Ok(EventTime::from(datetime.and_utc().timestamp_millis()));
123 }
124
125 let result = NaiveDateTime::parse_from_str(self, "%Y-%m-%d %H:%M:%S%.3f");
126 if let Ok(datetime) = result {
127 return Ok(EventTime::from(datetime.and_utc().timestamp_millis()));
128 }
129
130 let result = NaiveDateTime::parse_from_str(self, "%Y-%m-%d %H:%M:%S%");
131 if let Ok(datetime) = result {
132 return Ok(EventTime::from(datetime.and_utc().timestamp_millis()));
133 }
134
135 Err(ParseTimeError::InvalidDateTimeString(self.to_string()))
136 }
137}
138
139pub trait TryIntoTimeNeedsEventId: TryIntoTime {}
140
141impl TryIntoTimeNeedsEventId for i64 {}
142
143impl<Tz: TimeZone> TryIntoTimeNeedsEventId for DateTime<Tz> {}
144
145impl TryIntoTimeNeedsEventId for NaiveDateTime {}
146
147impl TryIntoTimeNeedsEventId for &str {}
148
149pub enum InputTime {
152 Simple(i64),
153 Indexed(i64, usize),
154}
155
156impl InputTime {
157 pub fn set_index(self, index: usize) -> Self {
158 match self {
159 InputTime::Simple(time) => InputTime::Indexed(time, index),
160 InputTime::Indexed(time, _) => InputTime::Indexed(time, index),
161 }
162 }
163
164 pub fn as_time(&self) -> EventTime {
165 match self {
166 InputTime::Simple(t) => EventTime::new(*t, 0),
167 InputTime::Indexed(t, s) => EventTime::new(*t, *s),
168 }
169 }
170}
171
172pub trait AsSingleTimeInput {
174 fn try_into_input_time(self) -> Result<InputTime, ParseTimeError>;
175}
176
177impl<T: TryIntoTimeNeedsEventId> AsSingleTimeInput for T {
178 fn try_into_input_time(self) -> Result<InputTime, ParseTimeError> {
179 Ok(InputTime::Simple(self.try_into_time()?.t()))
180 }
181}
182
183pub trait TryIntoInputTime {
184 fn try_into_input_time(self) -> Result<InputTime, ParseTimeError>;
185}
186
187impl TryIntoInputTime for InputTime {
188 fn try_into_input_time(self) -> Result<InputTime, ParseTimeError> {
189 Ok(self)
190 }
191}
192
193impl<T: AsSingleTimeInput> TryIntoInputTime for T {
194 fn try_into_input_time(self) -> Result<InputTime, ParseTimeError> {
195 self.try_into_input_time()
196 }
197}
198
199impl TryIntoInputTime for EventTime {
200 fn try_into_input_time(self) -> Result<InputTime, ParseTimeError> {
201 Ok(InputTime::Indexed(self.t(), self.i()))
202 }
203}
204
205impl<T: AsSingleTimeInput> TryIntoInputTime for (T, usize) {
206 fn try_into_input_time(self) -> Result<InputTime, ParseTimeError> {
207 Ok(self.0.try_into_input_time()?.set_index(self.1))
208 }
209}
210
211pub trait IntoTimeWithFormat {
212 fn parse_time(&self, fmt: &str) -> Result<i64, ParseTimeError>;
213}
214
215impl IntoTimeWithFormat for &str {
216 fn parse_time(&self, fmt: &str) -> Result<i64, ParseTimeError> {
217 let timestamp = NaiveDateTime::parse_from_str(self, fmt)?
218 .and_utc()
219 .timestamp_millis();
220 Ok(timestamp)
221 }
222}