clickhouse_client/value/ext/
time.rs1use crate::{
4 error::Error,
5 value::{ChValue, Type, Value},
6};
7
8use time::PrimitiveDateTime;
9pub use time::{
10 format_description::FormatItem, macros::format_description, Date, Month, OffsetDateTime,
11};
12
13static FORMAT_YY_MM_DD: &[FormatItem] = format_description!("[year]-[month]-[day]");
17
18pub trait DateExt: Sized {
20 fn unix_days(&self) -> i32;
22
23 fn from_unix_days(days: i32) -> Result<Self, Error>
25 where
26 Self: Sized;
27
28 fn format_yyyy_mm_dd(&self) -> String;
30
31 fn parse_yyyy_mm_dd(value: &str) -> Result<Self, Error>;
33
34 fn unix_day0() -> Date {
36 Date::from_calendar_date(1970, Month::January, 1).unwrap()
37 }
38}
39
40impl DateExt for Date {
41 fn unix_days(&self) -> i32 {
42 self.to_julian_day() - Self::unix_day0().to_julian_day()
43 }
44
45 fn from_unix_days(days: i32) -> Result<Self, Error> {
46 let julian_days = Self::unix_day0().to_julian_day() + days;
47 Ok(Date::from_julian_day(julian_days)?)
48 }
49
50 fn format_yyyy_mm_dd(&self) -> String {
51 self.format(FORMAT_YY_MM_DD).unwrap()
52 }
53
54 fn parse_yyyy_mm_dd(value: &str) -> Result<Self, Error> {
55 Ok(Date::parse(value, FORMAT_YY_MM_DD)?)
56 }
57}
58
59impl ChValue for Date {
61 fn ch_type() -> crate::value::Type {
62 Type::Date32
63 }
64
65 fn into_ch_value(self) -> Value {
66 Value::Date32(self.unix_days())
67 }
68
69 fn from_ch_value(value: Value) -> Result<Self, Error> {
70 match value {
71 Value::Date(v) => Ok(Date::from_unix_days(v.into())?),
72 Value::Date32(v) => Ok(Date::from_unix_days(v)?),
73 _ => Err(Error::new("Cannot convert Value to base type")),
74 }
75 }
76}
77
78pub trait DateTimeExt: Sized {
82 fn format_yyyy_mm_dd_hh_mm_ss(&self) -> String;
84
85 fn format_yyyy_mm_dd_hh_mm_ss_ns(&self) -> String;
87
88 fn parse_yyyy_mm_dd_hh_mm_ss(value: &str) -> Result<Self, Error>;
90
91 fn parse_yyyy_mm_dd_hh_mm_ss_ns(value: &str) -> Result<Self, Error>;
93
94 fn unix_seconds(&self) -> i64;
96
97 fn unix_nanoseconds(&self) -> i64;
99
100 fn from_unix_seconds(value: i64) -> Self;
102
103 fn from_unix_nanoseconds(value: i128) -> Self;
105}
106
107static FORMAT_YY_MM_DD_HH_MM_SS: &[FormatItem] =
109 format_description!("[year]-[month]-[day] [hour]:[minute]:[second]");
110
111static FORMAT_YY_MM_DD_HH_MM_SS_NS: &[FormatItem] =
113 format_description!("[year]-[month]-[day] [hour]:[minute]:[second].[subsecond]");
114
115impl DateTimeExt for PrimitiveDateTime {
116 fn format_yyyy_mm_dd_hh_mm_ss(&self) -> String {
117 self.format(FORMAT_YY_MM_DD_HH_MM_SS).unwrap()
118 }
119
120 fn format_yyyy_mm_dd_hh_mm_ss_ns(&self) -> String {
121 self.format(FORMAT_YY_MM_DD_HH_MM_SS_NS).unwrap()
122 }
123
124 fn parse_yyyy_mm_dd_hh_mm_ss(value: &str) -> Result<Self, Error> {
125 Ok(PrimitiveDateTime::parse(value, FORMAT_YY_MM_DD_HH_MM_SS)?)
126 }
127
128 fn parse_yyyy_mm_dd_hh_mm_ss_ns(value: &str) -> Result<Self, Error> {
129 Ok(PrimitiveDateTime::parse(
130 value,
131 FORMAT_YY_MM_DD_HH_MM_SS_NS,
132 )?)
133 }
134
135 fn unix_seconds(&self) -> i64 {
136 self.assume_utc().unix_seconds()
137 }
138
139 fn unix_nanoseconds(&self) -> i64 {
140 self.assume_utc().unix_nanoseconds()
141 }
142
143 fn from_unix_seconds(value: i64) -> Self {
144 let dt = OffsetDateTime::from_unix_seconds(value);
145 let date = dt.date();
146 let time = dt.time();
147 PrimitiveDateTime::new(date, time)
148 }
149
150 fn from_unix_nanoseconds(value: i128) -> Self {
151 let dt = OffsetDateTime::from_unix_nanoseconds(value);
152 let date = dt.date();
153 let time = dt.time();
154 PrimitiveDateTime::new(date, time)
155 }
156}
157
158impl ChValue for PrimitiveDateTime {
160 fn ch_type() -> Type {
161 Type::DateTime64(9)
162 }
163
164 fn into_ch_value(self) -> Value {
165 Value::DateTime64(self.unix_nanoseconds())
166 }
167
168 fn from_ch_value(value: Value) -> Result<Self, Error> {
169 match value {
170 Value::DateTime(secs) => Ok(Self::from_unix_seconds(secs.into())),
171 Value::DateTime64(nanosecs) => Ok(Self::from_unix_nanoseconds(nanosecs.into())),
172 _ => Err(Error::new("Cannot convert Value to base type")),
173 }
174 }
175}
176
177impl DateTimeExt for OffsetDateTime {
180 fn format_yyyy_mm_dd_hh_mm_ss(&self) -> String {
181 self.format(FORMAT_YY_MM_DD_HH_MM_SS).unwrap()
182 }
183
184 fn format_yyyy_mm_dd_hh_mm_ss_ns(&self) -> String {
185 self.format(FORMAT_YY_MM_DD_HH_MM_SS_NS).unwrap()
186 }
187
188 fn parse_yyyy_mm_dd_hh_mm_ss(value: &str) -> Result<Self, Error> {
189 Ok(PrimitiveDateTime::parse_yyyy_mm_dd_hh_mm_ss(value)?.assume_utc())
190 }
191
192 fn parse_yyyy_mm_dd_hh_mm_ss_ns(value: &str) -> Result<Self, Error> {
193 Ok(PrimitiveDateTime::parse_yyyy_mm_dd_hh_mm_ss_ns(value)?.assume_utc())
194 }
195
196 fn unix_seconds(&self) -> i64 {
197 self.unix_timestamp()
198 }
199
200 fn unix_nanoseconds(&self) -> i64 {
201 let ns = self.unix_timestamp_nanos();
202 if ns <= i64::MIN as i128 {
203 i64::MIN
204 } else if ns >= i64::MAX as i128 {
205 i64::MAX
206 } else {
207 ns as i64
208 }
209 }
210
211 fn from_unix_seconds(value: i64) -> Self {
212 Self::from_unix_timestamp(value).expect("invalid unix timestamp")
213 }
214
215 fn from_unix_nanoseconds(value: i128) -> Self {
216 Self::from_unix_timestamp_nanos(value).expect("invalid unix timestamp")
217 }
218}
219
220impl ChValue for OffsetDateTime {
222 fn ch_type() -> Type {
223 Type::DateTime64(9)
224 }
225
226 fn into_ch_value(self) -> Value {
227 Value::DateTime64(self.unix_nanoseconds())
228 }
229
230 fn from_ch_value(value: Value) -> Result<Self, Error> {
231 match value {
232 Value::DateTime(secs) => Ok(Self::from_unix_seconds(secs.into())),
233 Value::DateTime64(nanosecs) => Ok(Self::from_unix_nanoseconds(nanosecs.into())),
234 _ => Err(Error::new("Cannot convert Value to base type")),
235 }
236 }
237}
238
239impl ChValue for Option<Date> {
242 fn ch_type() -> Type {
243 Type::NullableDate32
244 }
245
246 fn into_ch_value(self) -> Value {
247 match self {
248 Some(v) => Value::NullableDate32(Some(v.unix_days())),
249 None => Value::NullableDate32(None),
250 }
251 }
252
253 fn from_ch_value(value: Value) -> Result<Self, Error> {
254 match value {
255 Value::NullableDate(v) => match v {
256 Some(v) => Date::from_unix_days(v.into()).map(Some),
257 None => Ok(None),
258 },
259 Value::NullableDate32(v) => match v {
260 Some(v) => Date::from_unix_days(v).map(Some),
261 None => Ok(None),
262 },
263 _ => Err(Error::new("Cannot convert Value to base type")),
264 }
265 }
266}
267
268impl ChValue for Option<PrimitiveDateTime> {
271 fn ch_type() -> Type {
272 Type::NullableDateTime64(9)
273 }
274
275 fn into_ch_value(self) -> Value {
276 match self {
277 Some(v) => PrimitiveDateTime::into_ch_value(v),
278 None => Value::NullableDateTime64(None),
279 }
280 }
281
282 fn from_ch_value(value: Value) -> Result<Self, Error> {
283 match value {
284 Value::NullableDateTime(secs) => match secs {
285 Some(secs) => Ok(Some(PrimitiveDateTime::from_unix_seconds(secs.into()))),
286 None => Ok(None),
287 },
288 Value::NullableDateTime64(nanosecs) => match nanosecs {
289 Some(nanosecs) => Ok(Some(PrimitiveDateTime::from_unix_nanoseconds(
290 nanosecs.into(),
291 ))),
292 None => Ok(None),
293 },
294 _ => Err(Error::new("Cannot convert Value to base type")),
295 }
296 }
297}
298
299impl ChValue for Option<OffsetDateTime> {
302 fn ch_type() -> Type {
303 Type::NullableDateTime64(9)
304 }
305
306 fn into_ch_value(self) -> Value {
307 match self {
308 Some(v) => OffsetDateTime::into_ch_value(v),
309 None => Value::NullableDateTime64(None),
310 }
311 }
312
313 fn from_ch_value(value: Value) -> Result<Self, Error> {
314 match value {
315 Value::NullableDateTime(secs) => match secs {
316 Some(secs) => Ok(Some(OffsetDateTime::from_unix_seconds(secs.into()))),
317 None => Ok(None),
318 },
319 Value::NullableDateTime64(nanosecs) => match nanosecs {
320 Some(nanosecs) => Ok(Some(OffsetDateTime::from_unix_nanoseconds(nanosecs.into()))),
321 None => Ok(None),
322 },
323 _ => Err(Error::new("Cannot convert Value to base type")),
324 }
325 }
326}