servers/postgres/types/
datetime.rs

1// Copyright 2023 Greptime Team
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use bytes::BufMut;
16use chrono::{NaiveDate, NaiveDateTime};
17use pgwire::types::ToSqlText;
18use postgres_types::{IsNull, ToSql, Type};
19use session::session_config::{PGDateOrder, PGDateTimeStyle};
20
21#[derive(Debug)]
22pub struct StylingDate(pub NaiveDate, pub PGDateTimeStyle, pub PGDateOrder);
23
24#[derive(Debug)]
25pub struct StylingDateTime(pub NaiveDateTime, pub PGDateTimeStyle, pub PGDateOrder);
26
27fn date_format_string(style: PGDateTimeStyle, order: PGDateOrder) -> &'static str {
28    match style {
29        PGDateTimeStyle::ISO => "%Y-%m-%d",
30        PGDateTimeStyle::German => "%d.%m.%Y",
31        PGDateTimeStyle::Postgres => match order {
32            PGDateOrder::MDY | PGDateOrder::YMD => "%m-%d-%Y",
33            PGDateOrder::DMY => "%d-%m-%Y",
34        },
35        PGDateTimeStyle::SQL => match order {
36            PGDateOrder::MDY | PGDateOrder::YMD => "%m/%d/%Y",
37            PGDateOrder::DMY => "%d/%m/%Y",
38        },
39    }
40}
41
42fn datetime_format_string(style: PGDateTimeStyle, order: PGDateOrder) -> &'static str {
43    match style {
44        PGDateTimeStyle::ISO => "%Y-%m-%d %H:%M:%S%.6f",
45        PGDateTimeStyle::German => "%d.%m.%Y %H:%M:%S%.6f",
46        PGDateTimeStyle::Postgres => match order {
47            PGDateOrder::MDY | PGDateOrder::YMD => "%a %b %d %H:%M:%S%.6f %Y",
48            PGDateOrder::DMY => "%a %d %b %H:%M:%S%.6f %Y",
49        },
50        PGDateTimeStyle::SQL => match order {
51            PGDateOrder::MDY | PGDateOrder::YMD => "%m/%d/%Y %H:%M:%S%.6f",
52            PGDateOrder::DMY => "%d/%m/%Y %H:%M:%S%.6f",
53        },
54    }
55}
56impl ToSqlText for StylingDate {
57    fn to_sql_text(
58        &self,
59        ty: &Type,
60        out: &mut bytes::BytesMut,
61    ) -> std::result::Result<IsNull, Box<dyn std::error::Error + Sync + Send>>
62    where
63        Self: Sized,
64    {
65        match *ty {
66            Type::DATE => {
67                let fmt = self
68                    .0
69                    .format(date_format_string(self.1, self.2))
70                    .to_string();
71                out.put_slice(fmt.as_bytes());
72            }
73            _ => {
74                self.0.to_sql_text(ty, out)?;
75            }
76        }
77        Ok(IsNull::No)
78    }
79}
80
81impl ToSqlText for StylingDateTime {
82    fn to_sql_text(
83        &self,
84        ty: &Type,
85        out: &mut bytes::BytesMut,
86    ) -> Result<IsNull, Box<dyn std::error::Error + Sync + Send>>
87    where
88        Self: Sized,
89    {
90        match *ty {
91            Type::TIMESTAMP => {
92                let fmt = self
93                    .0
94                    .format(datetime_format_string(self.1, self.2))
95                    .to_string();
96                out.put_slice(fmt.as_bytes());
97            }
98            Type::DATE => {
99                let fmt = self
100                    .0
101                    .format(date_format_string(self.1, self.2))
102                    .to_string();
103                out.put_slice(fmt.as_bytes());
104            }
105            _ => {
106                self.0.to_sql_text(ty, out)?;
107            }
108        }
109        Ok(IsNull::No)
110    }
111}
112
113macro_rules! delegate_to_sql {
114    ($delegator:ident, $delegatee:ident) => {
115        impl ToSql for $delegator {
116            fn to_sql(
117                &self,
118                ty: &Type,
119                out: &mut bytes::BytesMut,
120            ) -> std::result::Result<IsNull, Box<dyn std::error::Error + Sync + Send>> {
121                self.0.to_sql(ty, out)
122            }
123
124            fn accepts(ty: &Type) -> bool {
125                <$delegatee as ToSql>::accepts(ty)
126            }
127
128            fn to_sql_checked(
129                &self,
130                ty: &Type,
131                out: &mut bytes::BytesMut,
132            ) -> Result<IsNull, Box<dyn std::error::Error + Sync + Send>> {
133                self.0.to_sql_checked(ty, out)
134            }
135        }
136    };
137}
138
139delegate_to_sql!(StylingDate, NaiveDate);
140delegate_to_sql!(StylingDateTime, NaiveDateTime);
141
142#[cfg(test)]
143mod tests {
144    use super::*;
145
146    #[test]
147    fn test_styling_date() {
148        let naive_date = NaiveDate::from_ymd_opt(1997, 12, 17).unwrap();
149
150        {
151            let styling_date = StylingDate(naive_date, PGDateTimeStyle::ISO, PGDateOrder::MDY);
152            let expected = "1997-12-17";
153            let mut out = bytes::BytesMut::new();
154            let is_null = styling_date.to_sql_text(&Type::DATE, &mut out).unwrap();
155            assert!(matches!(is_null, IsNull::No));
156            assert_eq!(out, expected.as_bytes());
157        }
158
159        {
160            let styling_date = StylingDate(naive_date, PGDateTimeStyle::ISO, PGDateOrder::YMD);
161            let expected = "1997-12-17";
162            let mut out = bytes::BytesMut::new();
163            let is_null = styling_date.to_sql_text(&Type::DATE, &mut out).unwrap();
164            assert!(matches!(is_null, IsNull::No));
165            assert_eq!(out, expected.as_bytes());
166        }
167
168        {
169            let styling_date = StylingDate(naive_date, PGDateTimeStyle::ISO, PGDateOrder::DMY);
170            let expected = "1997-12-17";
171            let mut out = bytes::BytesMut::new();
172            let is_null = styling_date.to_sql_text(&Type::DATE, &mut out).unwrap();
173            assert!(matches!(is_null, IsNull::No));
174            assert_eq!(out, expected.as_bytes());
175        }
176
177        {
178            let styling_date = StylingDate(naive_date, PGDateTimeStyle::German, PGDateOrder::MDY);
179            let expected = "17.12.1997";
180            let mut out = bytes::BytesMut::new();
181            let is_null = styling_date.to_sql_text(&Type::DATE, &mut out).unwrap();
182            assert!(matches!(is_null, IsNull::No));
183            assert_eq!(out, expected.as_bytes());
184        }
185
186        {
187            let styling_date = StylingDate(naive_date, PGDateTimeStyle::German, PGDateOrder::YMD);
188            let expected = "17.12.1997";
189            let mut out = bytes::BytesMut::new();
190            let is_null = styling_date.to_sql_text(&Type::DATE, &mut out).unwrap();
191            assert!(matches!(is_null, IsNull::No));
192            assert_eq!(out, expected.as_bytes());
193        }
194
195        {
196            let styling_date = StylingDate(naive_date, PGDateTimeStyle::German, PGDateOrder::DMY);
197            let expected = "17.12.1997";
198            let mut out = bytes::BytesMut::new();
199            let is_null = styling_date.to_sql_text(&Type::DATE, &mut out).unwrap();
200            assert!(matches!(is_null, IsNull::No));
201            assert_eq!(out, expected.as_bytes());
202        }
203
204        {
205            let styling_date = StylingDate(naive_date, PGDateTimeStyle::Postgres, PGDateOrder::MDY);
206            let expected = "12-17-1997";
207            let mut out = bytes::BytesMut::new();
208            let is_null = styling_date.to_sql_text(&Type::DATE, &mut out).unwrap();
209            assert!(matches!(is_null, IsNull::No));
210            assert_eq!(out, expected.as_bytes());
211        }
212
213        {
214            let styling_date = StylingDate(naive_date, PGDateTimeStyle::Postgres, PGDateOrder::YMD);
215            let expected = "12-17-1997";
216            let mut out = bytes::BytesMut::new();
217            let is_null = styling_date.to_sql_text(&Type::DATE, &mut out).unwrap();
218            assert!(matches!(is_null, IsNull::No));
219            assert_eq!(out, expected.as_bytes());
220        }
221
222        {
223            let styling_date = StylingDate(naive_date, PGDateTimeStyle::Postgres, PGDateOrder::DMY);
224            let expected = "17-12-1997";
225            let mut out = bytes::BytesMut::new();
226            let is_null = styling_date.to_sql_text(&Type::DATE, &mut out).unwrap();
227            assert!(matches!(is_null, IsNull::No));
228            assert_eq!(out, expected.as_bytes());
229        }
230
231        {
232            let styling_date = StylingDate(naive_date, PGDateTimeStyle::SQL, PGDateOrder::MDY);
233            let expected = "12/17/1997";
234            let mut out = bytes::BytesMut::new();
235            let is_null = styling_date.to_sql_text(&Type::DATE, &mut out).unwrap();
236            assert!(matches!(is_null, IsNull::No));
237            assert_eq!(out, expected.as_bytes());
238        }
239
240        {
241            let styling_date = StylingDate(naive_date, PGDateTimeStyle::SQL, PGDateOrder::YMD);
242            let expected = "12/17/1997";
243            let mut out = bytes::BytesMut::new();
244            let is_null = styling_date.to_sql_text(&Type::DATE, &mut out).unwrap();
245            assert!(matches!(is_null, IsNull::No));
246            assert_eq!(out, expected.as_bytes());
247        }
248
249        {
250            let styling_date = StylingDate(naive_date, PGDateTimeStyle::SQL, PGDateOrder::DMY);
251            let expected = "17/12/1997";
252            let mut out = bytes::BytesMut::new();
253            let is_null = styling_date.to_sql_text(&Type::DATE, &mut out).unwrap();
254            assert!(matches!(is_null, IsNull::No));
255            assert_eq!(out, expected.as_bytes());
256        }
257    }
258
259    #[test]
260    fn test_styling_datetime() {
261        let input =
262            NaiveDateTime::parse_from_str("2021-09-01 12:34:56.789012", "%Y-%m-%d %H:%M:%S%.f")
263                .unwrap();
264
265        {
266            let styling_datetime = StylingDateTime(input, PGDateTimeStyle::ISO, PGDateOrder::MDY);
267            let expected = "2021-09-01 12:34:56.789012";
268            let mut out = bytes::BytesMut::new();
269            let is_null = styling_datetime
270                .to_sql_text(&Type::TIMESTAMP, &mut out)
271                .unwrap();
272            assert!(matches!(is_null, IsNull::No));
273            assert_eq!(out, expected.as_bytes());
274        }
275
276        {
277            let styling_datetime = StylingDateTime(input, PGDateTimeStyle::ISO, PGDateOrder::YMD);
278            let expected = "2021-09-01 12:34:56.789012";
279            let mut out = bytes::BytesMut::new();
280            let is_null = styling_datetime
281                .to_sql_text(&Type::TIMESTAMP, &mut out)
282                .unwrap();
283            assert!(matches!(is_null, IsNull::No));
284            assert_eq!(out, expected.as_bytes());
285        }
286
287        {
288            let styling_datetime = StylingDateTime(input, PGDateTimeStyle::ISO, PGDateOrder::DMY);
289            let expected = "2021-09-01 12:34:56.789012";
290            let mut out = bytes::BytesMut::new();
291            let is_null = styling_datetime
292                .to_sql_text(&Type::TIMESTAMP, &mut out)
293                .unwrap();
294            assert!(matches!(is_null, IsNull::No));
295            assert_eq!(out, expected.as_bytes());
296        }
297
298        {
299            let styling_datetime =
300                StylingDateTime(input, PGDateTimeStyle::German, PGDateOrder::MDY);
301            let expected = "01.09.2021 12:34:56.789012";
302            let mut out = bytes::BytesMut::new();
303            let is_null = styling_datetime
304                .to_sql_text(&Type::TIMESTAMP, &mut out)
305                .unwrap();
306            assert!(matches!(is_null, IsNull::No));
307            assert_eq!(out, expected.as_bytes());
308        }
309
310        {
311            let styling_datetime =
312                StylingDateTime(input, PGDateTimeStyle::German, PGDateOrder::YMD);
313            let expected = "01.09.2021 12:34:56.789012";
314            let mut out = bytes::BytesMut::new();
315            let is_null = styling_datetime
316                .to_sql_text(&Type::TIMESTAMP, &mut out)
317                .unwrap();
318            assert!(matches!(is_null, IsNull::No));
319            assert_eq!(out, expected.as_bytes());
320        }
321
322        {
323            let styling_datetime =
324                StylingDateTime(input, PGDateTimeStyle::German, PGDateOrder::DMY);
325            let expected = "01.09.2021 12:34:56.789012";
326            let mut out = bytes::BytesMut::new();
327            let is_null = styling_datetime
328                .to_sql_text(&Type::TIMESTAMP, &mut out)
329                .unwrap();
330            assert!(matches!(is_null, IsNull::No));
331            assert_eq!(out, expected.as_bytes());
332        }
333
334        {
335            let styling_datetime =
336                StylingDateTime(input, PGDateTimeStyle::Postgres, PGDateOrder::MDY);
337            let expected = "Wed Sep 01 12:34:56.789012 2021";
338            let mut out = bytes::BytesMut::new();
339            let is_null = styling_datetime
340                .to_sql_text(&Type::TIMESTAMP, &mut out)
341                .unwrap();
342            assert!(matches!(is_null, IsNull::No));
343            assert_eq!(out, expected.as_bytes());
344        }
345
346        {
347            let styling_datetime =
348                StylingDateTime(input, PGDateTimeStyle::Postgres, PGDateOrder::YMD);
349            let expected = "Wed Sep 01 12:34:56.789012 2021";
350            let mut out = bytes::BytesMut::new();
351            let is_null = styling_datetime
352                .to_sql_text(&Type::TIMESTAMP, &mut out)
353                .unwrap();
354            assert!(matches!(is_null, IsNull::No));
355            assert_eq!(out, expected.as_bytes());
356        }
357
358        {
359            let styling_datetime =
360                StylingDateTime(input, PGDateTimeStyle::Postgres, PGDateOrder::DMY);
361            let expected = "Wed 01 Sep 12:34:56.789012 2021";
362            let mut out = bytes::BytesMut::new();
363            let is_null = styling_datetime
364                .to_sql_text(&Type::TIMESTAMP, &mut out)
365                .unwrap();
366            assert!(matches!(is_null, IsNull::No));
367            assert_eq!(out, expected.as_bytes());
368        }
369
370        {
371            let styling_datetime = StylingDateTime(input, PGDateTimeStyle::SQL, PGDateOrder::MDY);
372            let expected = "09/01/2021 12:34:56.789012";
373            let mut out = bytes::BytesMut::new();
374            let is_null = styling_datetime
375                .to_sql_text(&Type::TIMESTAMP, &mut out)
376                .unwrap();
377            assert!(matches!(is_null, IsNull::No));
378            assert_eq!(out, expected.as_bytes());
379        }
380
381        {
382            let styling_datetime = StylingDateTime(input, PGDateTimeStyle::SQL, PGDateOrder::YMD);
383            let expected = "09/01/2021 12:34:56.789012";
384            let mut out = bytes::BytesMut::new();
385            let is_null = styling_datetime
386                .to_sql_text(&Type::TIMESTAMP, &mut out)
387                .unwrap();
388            assert!(matches!(is_null, IsNull::No));
389            assert_eq!(out, expected.as_bytes());
390        }
391
392        {
393            let styling_datetime = StylingDateTime(input, PGDateTimeStyle::SQL, PGDateOrder::DMY);
394            let expected = "01/09/2021 12:34:56.789012";
395            let mut out = bytes::BytesMut::new();
396            let is_null = styling_datetime
397                .to_sql_text(&Type::TIMESTAMP, &mut out)
398                .unwrap();
399            assert!(matches!(is_null, IsNull::No));
400            assert_eq!(out, expected.as_bytes());
401        }
402    }
403}