skrillax_serde/
time.rs

1//! Time related convenience structures and serialization/deserialization
2//! implementations, as time in Silkroad Online may be a little tricky to
3//! display.
4#![cfg(feature = "chrono")]
5
6use crate::{ByteSize, Deserialize, SerializationError, Serialize};
7use byteorder::{LittleEndian, ReadBytesExt};
8use bytes::{BufMut, BytesMut};
9use chrono::{DateTime, Datelike, Duration as CDuration, TimeZone, Timelike, Utc};
10use std::io::Read;
11use std::ops::{Add, Deref};
12use std::time::Duration;
13
14/// A date time for Silkroad Online, which assumes the time is _after_
15/// 2000-01-01 00:00. This time should only be used as a representation for time
16/// to be serialized/deserialized and not for actual timekeeping. Instead, use
17/// the [From]/[Deref] traits to convert to or from this type right before or
18/// after transferring it over the network. This is essentially just a newtype
19/// around the [chrono::DateTime] type.
20///
21/// Also note that there are serialization/deserialization implementations for
22/// [chrono::DateTime] as well, which behaves differently. This is another quirk
23/// of Silkroad Online; having multiple ways of representing a date time. A
24/// [SilkroadTime] takes 4 bytes, while a [DateTime] will be serialized into 16
25/// bytes. Therefor, depending on the data type, one or the other may be chosen.
26#[derive(Copy, Clone, Debug)]
27pub struct SilkroadTime(DateTime<Utc>);
28
29impl SilkroadTime {
30    pub fn as_u32(&self) -> u32 {
31        ((self.year() - 2000) as u32) & 63
32            | ((self.month() - 1) & 15) << 6
33            | ((self.day() - 1) & 31) << 10
34            | (self.hour() & 31) << 15
35            | (self.minute() & 63) << 20
36            | (self.second() & 63) << 26
37    }
38
39    pub fn from_u32(data: u32) -> Self {
40        let year = (data & 63) + 2000;
41        let month = ((data >> 6) & 15) + 1;
42        let day = ((data >> 10) & 31) + 1;
43        let hours = (data >> 15) & 31;
44        let minute = (data >> 20) & 63;
45        let second = (data >> 26) & 63;
46        SilkroadTime(
47            Utc.with_ymd_and_hms(year as i32, month, day, hours, minute, second)
48                .unwrap(),
49        )
50    }
51}
52
53impl Default for SilkroadTime {
54    fn default() -> Self {
55        SilkroadTime(Utc::now())
56    }
57}
58
59impl From<DateTime<Utc>> for SilkroadTime {
60    fn from(time: DateTime<Utc>) -> Self {
61        Self(time)
62    }
63}
64
65impl From<Duration> for SilkroadTime {
66    fn from(duration: Duration) -> Self {
67        let start = Utc.with_ymd_and_hms(2000, 1, 1, 0, 0, 0).unwrap();
68        let new = start.add(CDuration::from_std(duration).unwrap());
69        SilkroadTime(new)
70    }
71}
72
73impl Deref for SilkroadTime {
74    type Target = DateTime<Utc>;
75
76    fn deref(&self) -> &Self::Target {
77        &self.0
78    }
79}
80
81impl Serialize for SilkroadTime {
82    fn write_to(&self, writer: &mut BytesMut) {
83        self.as_u32().write_to(writer)
84    }
85}
86
87impl ByteSize for SilkroadTime {
88    fn byte_size(&self) -> usize {
89        4
90    }
91}
92
93impl Deserialize for SilkroadTime {
94    fn read_from<T: Read + ReadBytesExt>(reader: &mut T) -> Result<Self, SerializationError>
95    where
96        Self: Sized,
97    {
98        let data = reader.read_u32::<LittleEndian>()?;
99        Ok(SilkroadTime::from_u32(data))
100    }
101}
102
103impl<T: TimeZone> Serialize for DateTime<T> {
104    fn write_to(&self, writer: &mut BytesMut) {
105        let utc_time = self.to_utc();
106        writer.put_u16_le(utc_time.year() as u16);
107        writer.put_u16_le(utc_time.month() as u16);
108        writer.put_u16_le(utc_time.day() as u16);
109        writer.put_u16_le(utc_time.hour() as u16);
110        writer.put_u16_le(utc_time.minute() as u16);
111        writer.put_u16_le(utc_time.second() as u16);
112        writer.put_u32_le(utc_time.timestamp_millis() as u32);
113    }
114}
115
116impl<T: TimeZone> ByteSize for DateTime<T> {
117    fn byte_size(&self) -> usize {
118        16
119    }
120}
121
122impl Deserialize for DateTime<Utc> {
123    fn read_from<T: Read + ReadBytesExt>(reader: &mut T) -> Result<Self, SerializationError> {
124        let timestamp = Utc
125            .with_ymd_and_hms(
126                reader.read_u16::<LittleEndian>()? as i32,
127                reader.read_u16::<LittleEndian>()? as u32,
128                reader.read_u16::<LittleEndian>()? as u32,
129                reader.read_u16::<LittleEndian>()? as u32,
130                reader.read_u16::<LittleEndian>()? as u32,
131                reader.read_u16::<LittleEndian>()? as u32,
132            )
133            .unwrap();
134        Ok(timestamp + Duration::from_millis(reader.read_u32::<LittleEndian>()? as u64))
135    }
136}
137
138#[cfg(test)]
139mod test {
140    use super::*;
141
142    #[test]
143    pub fn test_convert_time() {
144        let one_year = 60 * 60 * 24 * 366u64;
145        let one_day = 60 * 60 * 24u64;
146
147        let time_now = Duration::from_secs(one_year + one_day + 35);
148        let sro_time = SilkroadTime::from(time_now);
149        let mut bytes = BytesMut::new();
150        sro_time.write_to(&mut bytes);
151        let written_bytes = bytes.freeze();
152
153        assert_eq!(written_bytes.len(), 4);
154
155        let lowest = written_bytes[0];
156        assert_eq!(lowest, 1); // The lowest 6 bits contain the year since year 2000, thus should be 1
157
158        let second = written_bytes[1];
159        assert_eq!(second >> 2, 1); // We need to shift by two to get the day part from the second byte
160
161        let highest = written_bytes[3];
162        assert_eq!(highest >> 2, 35);
163    }
164
165    #[test]
166    pub fn test_to_u32() {
167        let time = SilkroadTime::from(Utc.with_ymd_and_hms(2001, 10, 20, 14, 24, 40).unwrap());
168        let res = time.as_u32();
169        assert_eq!(res, 2709999169);
170    }
171
172    #[test]
173    pub fn test_convert_time_back() {
174        let time = SilkroadTime::from_u32(2709999169);
175        assert_eq!(time.year(), 2001);
176        assert_eq!(time.month(), 10);
177        assert_eq!(time.day(), 20);
178        assert_eq!(time.hour(), 14);
179        assert_eq!(time.minute(), 24);
180        assert_eq!(time.second(), 40);
181    }
182}