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 { 0 } else { value }
113}
114
115fn format_time(time: &MysqlTime, f: &mut fmt::Formatter<'_>) -> fmt::Result {
117 if time.neg {
118 f.write_char('-')?;
119 }
120
121 write!(
122 f,
123 "{:02}:{:02}:{:02}",
124 time.hour,
125 trim_two_digits(time.minute),
126 trim_two_digits(time.second),
127 )?;
128 format_useconds(time.second_part, f)?;
129 Ok(())
130}
131
132fn format_datetime(time: &MysqlTime, f: &mut fmt::Formatter<'_>) -> fmt::Result {
134 format_date_and_time(time, f)?;
135 format_useconds(time.second_part, f)?;
136 if time.time_type == MysqlTimestampType::MYSQL_TIMESTAMP_DATETIME_TZ {
137 format_tz(time.time_zone_displacement, f)?;
138 }
139 Ok(())
140}
141
142fn format_date_and_time(time: &MysqlTime, f: &mut fmt::Formatter<'_>) -> fmt::Result {
144 write!(
145 f,
146 "{:02}{:02}-{:02}-{:02} {:02}:{:02}:{:02}",
147 trim_two_digits(time.year / 100),
148 trim_two_digits(time.year % 100),
149 trim_two_digits(time.month),
150 trim_two_digits(time.day),
151 trim_two_digits(time.hour),
152 trim_two_digits(time.minute),
153 trim_two_digits(time.second),
154 )
155}
156
157fn format_date(time: &MysqlTime, f: &mut fmt::Formatter<'_>) -> fmt::Result {
159 write!(
160 f,
161 "{:02}{:02}-{:02}-{:02}",
162 trim_two_digits(time.year / 100),
163 trim_two_digits(time.year % 100),
164 trim_two_digits(time.month),
165 trim_two_digits(time.day),
166 )
167}
168
169fn format_useconds(mut useconds: u32, f: &mut fmt::Formatter<'_>) -> fmt::Result {
171 let Some(dec) = f.precision().map(|x| min(x, 6)) else {
172 return Ok(());
173 };
174
175 if dec == 0 {
176 return Ok(());
177 }
178
179 useconds %= 1_000_000;
180
181 for _ in 0..(6 - dec) {
182 useconds /= 10;
183 }
184
185 write!(f, ".{:0width$}", useconds, width = dec)
186}
187
188fn format_tz(tzd: i32, f: &mut fmt::Formatter<'_>) -> fmt::Result {
189 write!(f, "+{:02}:{:02}", tzd / 3600, tzd.abs() / 60 % 60)
190}
191
192#[derive(Debug, Clone, PartialEq)]
193#[repr(C)]
194#[allow(non_camel_case_types)]
195pub enum MysqlTimestampType {
196 MYSQL_TIMESTAMP_NONE = -2,
198 MYSQL_TIMESTAMP_ERROR = -1,
200 MYSQL_TIMESTAMP_DATE = 0,
201 MYSQL_TIMESTAMP_DATETIME = 1,
202 MYSQL_TIMESTAMP_TIME = 2,
203 MYSQL_TIMESTAMP_DATETIME_TZ = 3,
204}