mssql_value_serializer/literals/
chrono.rs

1use std::fmt::{self, Formatter, Write};
2
3use chrono::prelude::*;
4
5use super::{SqlLiteralError, SqlServerLiteral};
6use crate::impl_dyn_wrapper;
7
8// ----- Date & Time -----
9
10#[inline]
11fn push_naive_date(naive_date: &NaiveDate, out: &mut impl Write) -> fmt::Result {
12    write!(
13        out,
14        "{year:04}-{month:02}-{day:02}",
15        year = naive_date.year(),
16        month = naive_date.month(),
17        day = naive_date.day()
18    )
19}
20
21fn push_naive_time(naive_time: &NaiveTime, out: &mut impl Write) -> fmt::Result {
22    write!(
23        out,
24        "{hour:02}:{minute:02}:{second:02}",
25        hour = naive_time.hour(),
26        minute = naive_time.minute(),
27        second = naive_time.second(),
28    )?;
29
30    let mut nnnnnnn = (naive_time.nanosecond() / 100) * 100;
31
32    if nnnnnnn > 0 {
33        out.write_char('.')?;
34
35        let digits = nnnnnnn.ilog10() + 1;
36
37        for _ in digits..9 {
38            out.write_char('0')?;
39        }
40
41        // trim trailing zeros
42        while nnnnnnn % 10 == 0 {
43            nnnnnnn /= 10;
44        }
45
46        write!(out, "{nnnnnnn}")?;
47    }
48
49    Ok(())
50}
51
52#[inline]
53fn push_naive_date_time(naive_date_time: &NaiveDateTime, out: &mut impl Write) -> fmt::Result {
54    let date = naive_date_time.date();
55    let time = naive_date_time.time();
56
57    push_naive_date(&date, out)?;
58    out.write_char(' ')?;
59    push_naive_time(&time, out)
60}
61
62fn push_time_zone(fixed_offset: &FixedOffset, out: &mut impl Write) -> fmt::Result {
63    let seconds = fixed_offset.local_minus_utc();
64
65    let (sign, abs_seconds) = if seconds >= 0 { ('+', seconds) } else { ('-', -seconds) };
66
67    let hours = abs_seconds / 3600;
68    let minutes = (abs_seconds % 3600) / 60;
69
70    // seconds should be zero for SQL Server (or ignore it in release)
71    debug_assert!(
72        abs_seconds % 60 == 0,
73        "the seconds part of {fixed_offset:?} should be zero for SQL Server"
74    );
75
76    write!(out, "{sign}{hours:02}:{minutes:02}")
77}
78
79#[inline]
80fn push_date_time_fixed_offset(
81    date_time: &DateTime<FixedOffset>,
82    out: &mut impl Write,
83) -> fmt::Result {
84    let ndt = date_time.naive_local();
85    let time_zone = date_time.timezone();
86
87    push_naive_date_time(&ndt, out)?;
88    out.write_char(' ')?;
89    push_time_zone(&time_zone, out)
90}
91
92#[inline]
93fn push_date_time_utc(date_time: &DateTime<Utc>, out: &mut impl Write) -> fmt::Result {
94    let ndt = date_time.naive_utc();
95
96    push_naive_date_time(&ndt, out)?;
97    out.write_str(" +00:00")
98}
99
100#[cfg(feature = "stable-local")]
101fn push_date_time_local(date_time: &DateTime<Local>, out: &mut impl Write) -> fmt::Result {
102    use std::sync::Once;
103
104    static INIT: Once = Once::new();
105    static mut TIME_ZONE_STRING: String = String::new();
106
107    INIT.call_once(|| {
108        println!("call once");
109        let fixed_offset = date_time.offset().fix();
110
111        #[allow(static_mut_refs)]
112        unsafe {
113            TIME_ZONE_STRING.reserve(7);
114
115            TIME_ZONE_STRING.push(' ');
116            push_time_zone(&fixed_offset, &mut TIME_ZONE_STRING).unwrap(); // should not panic because the output is a string
117        }
118    });
119
120    let ndt = date_time.naive_local();
121
122    push_naive_date_time(&ndt, out)?;
123
124    #[allow(static_mut_refs)]
125    out.write_str(unsafe { TIME_ZONE_STRING.as_str() })
126}
127
128#[cfg(not(feature = "stable-local"))]
129#[inline]
130fn push_date_time_local(date_time: &DateTime<Local>, out: &mut impl Write) -> fmt::Result {
131    let date_time = date_time.fixed_offset();
132
133    push_date_time_fixed_offset(&date_time, out)
134}
135
136macro_rules! impl_date_time_as_string {
137    ($ty:ty, $f:ident) => {
138        impl SqlServerLiteral for $ty {
139            #[inline]
140            fn append_sql_literal(&self, out: &mut String) -> Result<(), SqlLiteralError> {
141                out.push('\'');
142                $f(self, out).unwrap();
143                out.push('\'');
144
145                Ok(())
146            }
147
148            #[inline]
149            fn append_sql_literal_fmt(
150                &self,
151                out: &mut Formatter<'_>,
152            ) -> Result<(), SqlLiteralError> {
153                out.write_char('\'').unwrap();
154                $f(self, out).unwrap();
155                out.write_char('\'').unwrap();
156
157                Ok(())
158            }
159        }
160
161        impl_dyn_wrapper!($ty);
162    };
163}
164impl_date_time_as_string!(NaiveDate, push_naive_date);
165impl_date_time_as_string!(NaiveTime, push_naive_time);
166impl_date_time_as_string!(NaiveDateTime, push_naive_date_time);
167impl_date_time_as_string!(DateTime<FixedOffset>, push_date_time_fixed_offset);
168impl_date_time_as_string!(DateTime<Utc>, push_date_time_utc);
169impl_date_time_as_string!(DateTime<Local>, push_date_time_local);