1use std::{
2 cmp::min,
3 fmt::{self, Write},
4};
5
6use super::misc::{my_packed_time_get_frac_part, my_packed_time_get_int_part};
7
8#[derive(Debug, Clone, PartialEq)]
10#[repr(C)]
11pub struct MysqlTime {
12 pub year: u32,
13 pub month: u32,
14 pub day: u32,
15 pub hour: u32,
16 pub minute: u32,
17 pub second: u32,
18 pub second_part: u32,
20 pub neg: bool,
21 pub time_type: MysqlTimestampType,
22 pub time_zone_displacement: i32,
23}
24
25impl MysqlTime {
26 pub fn from_int64_time_packed(mut packed_value: i64) -> Self {
28 let neg = packed_value < 0;
29 if neg {
30 packed_value = -packed_value
31 }
32
33 let hms: i64 = my_packed_time_get_int_part(packed_value);
34
35 let hour = (hms >> 12) as u32 % (1 << 10); let minute = (hms >> 6) as u32 % (1 << 6); let second = hms as u32 % (1 << 6); let second_part = my_packed_time_get_frac_part(packed_value);
39
40 Self {
41 year: 0,
42 month: 0,
43 day: 0,
44 hour,
45 minute,
46 second,
47 second_part: second_part as u32,
48 neg,
49 time_type: MysqlTimestampType::MYSQL_TIMESTAMP_TIME,
50 time_zone_displacement: 0,
51 }
52 }
53
54 pub fn from_int64_date_packed(packed_value: i64) -> Self {
56 let mut this = Self::from_int64_datetime_packed(packed_value);
57 this.time_type = MysqlTimestampType::MYSQL_TIMESTAMP_DATE;
58 this
59 }
60
61 pub fn from_int64_datetime_packed(mut packed_value: i64) -> Self {
63 let neg = packed_value < 0;
64 if neg {
65 packed_value = -packed_value
66 }
67
68 let second_part = my_packed_time_get_frac_part(packed_value);
69 let ymdhms: i64 = my_packed_time_get_int_part(packed_value);
70
71 let ymd: i64 = ymdhms >> 17;
72 let ym: i64 = ymd >> 5;
73 let hms: i64 = ymdhms % (1 << 17);
74
75 let day = ymd % (1 << 5);
76 let month = ym % 13;
77 let year = (ym / 13) as _;
78
79 let second = hms % (1 << 6);
80 let minute = (hms >> 6) % (1 << 6);
81 let hour = (hms >> 12) as _;
82
83 Self {
84 year,
85 month: month as _,
86 day: day as _,
87 hour,
88 minute: minute as _,
89 second: second as _,
90 second_part: second_part as _,
91 neg,
92 time_type: MysqlTimestampType::MYSQL_TIMESTAMP_DATETIME,
93 time_zone_displacement: 0,
94 }
95 }
96}
97
98impl fmt::Display for MysqlTime {
99 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
100 match self.time_type {
101 MysqlTimestampType::MYSQL_TIMESTAMP_DATETIME
102 | MysqlTimestampType::MYSQL_TIMESTAMP_DATETIME_TZ => format_datetime(self, f),
103 MysqlTimestampType::MYSQL_TIMESTAMP_DATE => format_date(self, f),
104 MysqlTimestampType::MYSQL_TIMESTAMP_TIME => format_time(self, f),
105 MysqlTimestampType::MYSQL_TIMESTAMP_NONE
106 | MysqlTimestampType::MYSQL_TIMESTAMP_ERROR => Ok(()),
107 }
108 }
109}
110
111fn trim_two_digits(value: u32) -> u32 {
112 if value >= 100 {
113 0
114 } else {
115 value
116 }
117}
118
119fn format_time(time: &MysqlTime, f: &mut fmt::Formatter<'_>) -> fmt::Result {
121 if time.neg {
122 f.write_char('-')?;
123 }
124
125 write!(
126 f,
127 "{:02}:{:02}:{:02}",
128 time.hour,
129 trim_two_digits(time.minute),
130 trim_two_digits(time.second),
131 )?;
132 format_useconds(time.second_part, f)?;
133 Ok(())
134}
135
136fn format_datetime(time: &MysqlTime, f: &mut fmt::Formatter<'_>) -> fmt::Result {
138 format_date_and_time(time, f)?;
139 format_useconds(time.second_part, f)?;
140 if time.time_type == MysqlTimestampType::MYSQL_TIMESTAMP_DATETIME_TZ {
141 format_tz(time.time_zone_displacement, f)?;
142 }
143 Ok(())
144}
145
146fn format_date_and_time(time: &MysqlTime, f: &mut fmt::Formatter<'_>) -> fmt::Result {
148 write!(
149 f,
150 "{:02}{:02}-{:02}-{:02} {:02}:{:02}:{:02}",
151 trim_two_digits(time.year / 100),
152 trim_two_digits(time.year % 100),
153 trim_two_digits(time.month),
154 trim_two_digits(time.day),
155 trim_two_digits(time.hour),
156 trim_two_digits(time.minute),
157 trim_two_digits(time.second),
158 )
159}
160
161fn format_date(time: &MysqlTime, f: &mut fmt::Formatter<'_>) -> fmt::Result {
163 write!(
164 f,
165 "{:02}{:02}-{:02}-{:02}",
166 trim_two_digits(time.year / 100),
167 trim_two_digits(time.year % 100),
168 trim_two_digits(time.month),
169 trim_two_digits(time.day),
170 )
171}
172
173fn format_useconds(mut useconds: u32, f: &mut fmt::Formatter<'_>) -> fmt::Result {
175 let Some(dec) = f.precision().map(|x| min(x, 6)) else {
176 return Ok(());
177 };
178
179 if dec == 0 {
180 return Ok(());
181 }
182
183 useconds %= 1_000_000;
184
185 for _ in 0..(6 - dec) {
186 useconds /= 10;
187 }
188
189 write!(f, ".{:0width$}", useconds, width = dec)
190}
191
192fn format_tz(tzd: i32, f: &mut fmt::Formatter<'_>) -> fmt::Result {
193 write!(f, "+{:02}:{:02}", tzd / 3600, tzd.abs() / 60 % 60)
194}
195
196#[derive(Debug, Clone, PartialEq)]
197#[repr(C)]
198#[allow(non_camel_case_types)]
199pub enum MysqlTimestampType {
200 MYSQL_TIMESTAMP_NONE = -2,
202 MYSQL_TIMESTAMP_ERROR = -1,
204 MYSQL_TIMESTAMP_DATE = 0,
205 MYSQL_TIMESTAMP_DATETIME = 1,
206 MYSQL_TIMESTAMP_TIME = 2,
207 MYSQL_TIMESTAMP_DATETIME_TZ = 3,
208}