1use std::sync::Arc;
2
3use arrow::datatypes::DataType as ArrowDataType;
4
5use crate::{AbsoluteTimeRange, TimestampFormat};
6
7use super::TimeInt;
8
9#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, num_derive::FromPrimitive)]
11#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
12pub enum TimeType {
13 Sequence,
15
16 DurationNs,
18
19 TimestampNs,
21}
22
23impl std::fmt::Display for TimeType {
24 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25 match self {
26 Self::Sequence => f.write_str("sequence"),
27 Self::DurationNs => f.write_str("duration"),
28 Self::TimestampNs => f.write_str("timestamp"),
29 }
30 }
31}
32
33impl TimeType {
34 #[inline]
35 pub(crate) fn hash(&self) -> u64 {
36 match self {
37 Self::Sequence => 0,
38 Self::DurationNs => 1,
39 Self::TimestampNs => 2,
40 }
41 }
42
43 pub fn format_sequence(time_int: TimeInt) -> String {
44 Self::Sequence.format(time_int, TimestampFormat::utc())
45 }
46
47 pub fn parse_sequence(s: &str) -> Option<TimeInt> {
48 match s {
49 "<static>" | "static" => Some(TimeInt::STATIC),
50 "−∞" | "-inf" | "-infinity" => Some(TimeInt::MIN),
51 "∞" | "+∞" | "inf" | "infinity" => Some(TimeInt::MAX),
52 _ => {
53 let s = s.strip_prefix('#').unwrap_or(s);
54 re_format::parse_i64(s).map(TimeInt::new_temporal)
55 }
56 }
57 }
58
59 pub fn parse_time(&self, s: &str, timestamp_format: TimestampFormat) -> Option<TimeInt> {
61 match s.to_lowercase().as_str() {
62 "<static>" | "static" => Some(TimeInt::STATIC),
63 "−∞" | "-inf" | "-infinity" => Some(TimeInt::MIN),
64 "∞" | "+∞" | "inf" | "infinity" => Some(TimeInt::MAX),
65 _ => {
66 match self {
67 Self::Sequence => {
68 if let Some(s) = s.strip_prefix('#') {
69 TimeInt::try_from(re_format::parse_i64(s)?).ok()
70 } else {
71 TimeInt::try_from(re_format::parse_i64(s)?).ok()
72 }
73 }
74 Self::DurationNs => {
75 if let Some(nanos) = re_format::parse_i64(s) {
76 nanos.try_into().ok()
78 } else {
79 s.parse::<super::Duration>()
80 .ok()
81 .map(|duration| duration.into())
82 }
83 }
84 Self::TimestampNs => {
85 if let Some(nanos) = re_format::parse_i64(s) {
86 nanos.try_into().ok()
88 } else {
89 super::Timestamp::parse_with_format(s, timestamp_format)
91 .map(|timestamp| timestamp.into())
92 }
93 }
94 }
95 }
96 }
97 }
98
99 pub fn format(
100 &self,
101 time_int: impl Into<TimeInt>,
102 timestamp_format: TimestampFormat,
103 ) -> String {
104 let time_int = time_int.into();
105 match time_int {
106 TimeInt::STATIC => "<static>".into(),
107 TimeInt::MIN => "−∞".into(),
108 TimeInt::MAX => "+∞".into(),
109 _ => match self {
110 Self::Sequence => format!("#{}", re_format::format_int(time_int.as_i64())),
111 Self::DurationNs => super::Duration::from(time_int).format_secs(),
112 Self::TimestampNs => super::Timestamp::from(time_int).format(timestamp_format),
113 },
114 }
115 }
116
117 #[inline]
118 pub fn format_utc(&self, time_int: TimeInt) -> String {
119 self.format(time_int, TimestampFormat::utc())
120 }
121
122 #[inline]
123 pub fn format_range(
124 &self,
125 time_range: AbsoluteTimeRange,
126 timestamp_format: TimestampFormat,
127 ) -> String {
128 format!(
129 "{}..={}",
130 self.format(time_range.min(), timestamp_format),
131 self.format(time_range.max(), timestamp_format)
132 )
133 }
134
135 #[inline]
136 pub fn format_range_utc(&self, time_range: AbsoluteTimeRange) -> String {
137 self.format_range(time_range, TimestampFormat::utc())
138 }
139
140 #[inline]
142 pub fn datatype(self) -> ArrowDataType {
143 match self {
144 Self::Sequence => ArrowDataType::Int64,
145 Self::DurationNs => ArrowDataType::Duration(arrow::datatypes::TimeUnit::Nanosecond),
146 Self::TimestampNs => {
147 ArrowDataType::Timestamp(arrow::datatypes::TimeUnit::Nanosecond, None)
149 }
150 }
151 }
152
153 pub fn from_arrow_datatype(datatype: &ArrowDataType) -> Option<Self> {
154 match datatype {
155 ArrowDataType::Int64 => Some(Self::Sequence),
156 ArrowDataType::Duration(arrow::datatypes::TimeUnit::Nanosecond) => {
157 Some(Self::DurationNs)
158 }
159 ArrowDataType::Timestamp(arrow::datatypes::TimeUnit::Nanosecond, timezone) => {
160 if timezone.as_ref().is_none_or(|tz| tz.is_empty()) {
163 } else {
165 }
168
169 Some(Self::TimestampNs)
170 }
171 _ => None,
172 }
173 }
174
175 pub fn make_arrow_array(
177 self,
178 times: impl Into<arrow::buffer::ScalarBuffer<i64>>,
179 ) -> arrow::array::ArrayRef {
180 let times = times.into();
181 match self {
182 Self::Sequence => Arc::new(arrow::array::Int64Array::new(times, None)),
183 Self::DurationNs => Arc::new(arrow::array::DurationNanosecondArray::new(times, None)),
184 Self::TimestampNs => Arc::new(arrow::array::TimestampNanosecondArray::new(times, None)),
186 }
187 }
188
189 pub fn make_arrow_array_from_time_ints(
191 self,
192 times: impl Iterator<Item = TimeInt>,
193 ) -> arrow::array::ArrayRef {
194 match self {
195 Self::Sequence => Arc::new(
196 times
197 .map(|time| {
198 if time.is_static() {
199 None
200 } else {
201 Some(time.as_i64())
202 }
203 })
204 .collect::<arrow::array::Int64Array>(),
205 ),
206
207 Self::DurationNs => Arc::new(
208 times
209 .map(|time| {
210 if time.is_static() {
211 None
212 } else {
213 Some(time.as_i64())
214 }
215 })
216 .collect::<arrow::array::DurationNanosecondArray>(),
217 ),
218
219 Self::TimestampNs => Arc::new(
220 times
221 .map(|time| {
222 if time.is_static() {
223 None
224 } else {
225 Some(time.as_i64())
226 }
227 })
228 .collect::<arrow::array::TimestampNanosecondArray>(),
230 ),
231 }
232 }
233}
234
235#[cfg(test)]
236mod tests {
237 use crate::{TimeInt, TimeType};
238
239 #[test]
240 fn test_format_parse() {
241 let cases = [
242 (TimeInt::STATIC, "<static>"),
243 (TimeInt::MIN, "−∞"),
244 (TimeInt::MAX, "+∞"),
245 (TimeInt::new_temporal(-42), "#−42"),
246 (TimeInt::new_temporal(12345), "#12 345"),
247 ];
248
249 for (int, s) in cases {
250 assert_eq!(TimeType::format_sequence(int), s);
251 assert_eq!(TimeType::parse_sequence(s), Some(int));
252 }
253 }
254}