mssql_value_serializer/literals/
time.rs

1use std::fmt::{self, Formatter, Write};
2
3use time::{Date, OffsetDateTime, PrimitiveDateTime, Time, UtcDateTime, UtcOffset};
4
5use super::{SqlLiteralError, SqlServerLiteral};
6use crate::impl_dyn_wrapper;
7
8// ----- Date & Time -----
9
10#[inline]
11fn push_naive_date(naive_date: &Date, 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() as u8,
17        day = naive_date.day()
18    )
19}
20
21fn push_naive_time(naive_time: &Time, 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: &PrimitiveDateTime, 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: &UtcOffset, out: &mut impl Write) -> fmt::Result {
63    let mut hours = fixed_offset.whole_hours();
64    let mut minutes = fixed_offset.minutes_past_hour();
65
66    let sign = if hours >= 0 {
67        '+'
68    } else {
69        hours = -hours;
70        minutes = -minutes;
71
72        '-'
73    };
74
75    // seconds should be zero for SQL Server (or ignore it in release)
76    debug_assert!(
77        fixed_offset.seconds_past_minute() == 0,
78        "the seconds part of {fixed_offset:?} should be zero for SQL Server"
79    );
80
81    write!(out, "{sign}{hours:02}:{minutes:02}")
82}
83
84#[inline]
85fn push_date_time_utc(naive_date_time: &UtcDateTime, out: &mut impl Write) -> fmt::Result {
86    let date = naive_date_time.date();
87    let time = naive_date_time.time();
88
89    push_naive_date(&date, out)?;
90    out.write_char(' ')?;
91    push_naive_time(&time, out)?;
92
93    out.write_str(" +00:00")
94}
95
96#[inline]
97fn push_date_time_fixed_offset(date_time: &OffsetDateTime, out: &mut impl Write) -> fmt::Result {
98    let date = date_time.date();
99    let time = date_time.time();
100    let time_zone = date_time.offset();
101
102    push_naive_date(&date, out)?;
103    out.write_char(' ')?;
104    push_naive_time(&time, out)?;
105
106    out.write_char(' ')?;
107    push_time_zone(&time_zone, out)
108}
109
110macro_rules! impl_date_time_as_string {
111    ($ty:ty, $f:ident) => {
112        impl SqlServerLiteral for $ty {
113            #[inline]
114            fn append_sql_literal(&self, out: &mut String) -> Result<(), SqlLiteralError> {
115                out.push('\'');
116                $f(self, out).unwrap();
117                out.push('\'');
118
119                Ok(())
120            }
121
122            #[inline]
123            fn append_sql_literal_fmt(
124                &self,
125                out: &mut Formatter<'_>,
126            ) -> Result<(), SqlLiteralError> {
127                out.write_char('\'').unwrap();
128                $f(self, out).unwrap();
129                out.write_char('\'').unwrap();
130
131                Ok(())
132            }
133        }
134
135        impl_dyn_wrapper!($ty);
136    };
137}
138impl_date_time_as_string!(Date, push_naive_date);
139impl_date_time_as_string!(Time, push_naive_time);
140impl_date_time_as_string!(PrimitiveDateTime, push_naive_date_time);
141impl_date_time_as_string!(OffsetDateTime, push_date_time_fixed_offset);
142impl_date_time_as_string!(UtcDateTime, push_date_time_utc);