ion_rs/binary/
timestamp.rs1use std::io::Write;
4use std::ops::Neg;
5
6use arrayvec::ArrayVec;
7use chrono::{Datelike, Timelike};
8
9use crate::binary::decimal::DecimalBinaryEncoder;
10use crate::binary::raw_binary_writer::MAX_INLINE_LENGTH;
11use crate::binary::var_int::VarInt;
12use crate::binary::var_uint::VarUInt;
13use crate::result::IonResult;
14use crate::types::{Decimal, Mantissa, Precision, Timestamp};
15
16const MAX_TIMESTAMP_LENGTH: usize = 32;
17
18pub trait TimestampBinaryEncoder {
22 fn encode_timestamp(&mut self, timestamp: &Timestamp) -> IonResult<usize>;
28
29 fn encode_timestamp_value(&mut self, timestamp: &Timestamp) -> IonResult<usize>;
32}
33
34impl<W> TimestampBinaryEncoder for W
35where
36 W: Write,
37{
38 fn encode_timestamp(&mut self, timestamp: &Timestamp) -> IonResult<usize> {
39 const SECONDS_PER_MINUTE: f32 = 60f32;
40 let mut bytes_written: usize = 0;
41
42 let utc = timestamp.date_time;
49
50 if let Some(offset) = timestamp.offset {
53 let offset_seconds = offset.local_minus_utc();
55 let offset_minutes = (offset_seconds as f32 / SECONDS_PER_MINUTE).round() as i64;
56 bytes_written += VarInt::write_i64(self, offset_minutes)?;
57 } else {
58 bytes_written += VarInt::write_negative_zero(self)?;
60 }
61
62 bytes_written += VarUInt::write_u64(self, utc.year() as u64)?;
63
64 if timestamp.precision > Precision::Year {
66 bytes_written += VarUInt::write_u64(self, utc.month() as u64)?;
67 if timestamp.precision > Precision::Month {
68 bytes_written += VarUInt::write_u64(self, utc.day() as u64)?;
69 if timestamp.precision > Precision::Day {
70 bytes_written += VarUInt::write_u64(self, utc.hour() as u64)?;
71 bytes_written += VarUInt::write_u64(self, utc.minute() as u64)?;
72 if timestamp.precision > Precision::HourAndMinute {
73 bytes_written += VarUInt::write_u64(self, utc.second() as u64)?;
74 if let Some(ref mantissa) = timestamp.fractional_seconds {
75 match mantissa {
79 Mantissa::Digits(precision) => {
80 let scaled = utc.nanosecond() / 10u32.pow(9 - *precision); let exponent = (*precision as i64).neg(); let fractional = Decimal::new(scaled, exponent); bytes_written += self.encode_decimal(&fractional)?;
87 }
88 Mantissa::Arbitrary(decimal) => {
89 bytes_written += self.encode_decimal(decimal)?;
90 }
91 };
92 }
93 }
94 }
95 }
96 }
97
98 Ok(bytes_written)
99 }
100
101 fn encode_timestamp_value(&mut self, timestamp: &Timestamp) -> IonResult<usize> {
102 let mut bytes_written: usize = 0;
103
104 let mut encoded: ArrayVec<u8, MAX_TIMESTAMP_LENGTH> = ArrayVec::new();
107 encoded.encode_timestamp(timestamp)?;
108
109 let type_descriptor: u8;
111 if encoded.len() <= MAX_INLINE_LENGTH {
112 type_descriptor = 0x60 | encoded.len() as u8;
113 self.write_all(&[type_descriptor])?;
114 bytes_written += 1;
115 } else {
116 type_descriptor = 0x6E;
117 self.write_all(&[type_descriptor])?;
118 bytes_written += 1;
119 bytes_written += VarUInt::write_u64(self, encoded.len() as u64)?;
120 }
121
122 self.write_all(&encoded[..])?;
124 bytes_written += &encoded[..].len();
125
126 Ok(bytes_written)
127 }
128}
129
130#[cfg(test)]
131mod binary_timestamp_tests {
132 use super::*;
133 use crate::{reader, IonReader, IonType, ReaderBuilder};
134 use rstest::*;
135
136 #[rstest]
139 #[case::y2k_utc("2000-01-01T00:00:00+00:00", 9)]
140 #[case::seconds_utc("2021-01-08T14:12:36+00:00", 9)]
141 #[case::seconds_tz("2021-01-08T14:12:36-05:00", 10)]
142 #[case::millis_tz("2021-01-08T14:12:36.888-05:00", 13)]
143 #[case::micros_tz("2021-01-08T14:12:36.888888-05:00", 14)]
144 #[case::nanos_tz("2021-01-08T14:12:36.888888888-05:00", 16)]
145 fn timestamp_encoding_bytes_written(
146 #[case] input: &str,
147 #[case] expected: usize,
148 ) -> IonResult<()> {
149 let mut reader = ReaderBuilder::new().build(input).unwrap();
150 match reader.next().unwrap() {
151 reader::StreamItem::Value(IonType::Timestamp) => {
152 let timestamp = reader.read_timestamp().unwrap();
153 let mut buf = vec![];
154 let written = buf.encode_timestamp_value(×tamp)?;
155 assert_eq!(buf.len(), expected);
156 assert_eq!(written, expected);
157 }
158 _ => panic!(
159 "reader.next() should only return reader::StreamItem::Value(IonType::Timestamp)"
160 ),
161 }
162 Ok(())
163 }
164}