1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
use crate::types::*;
use chrono::{FixedOffset, NaiveTime, Offset, Timelike};
use neo4rs_macros::BoltStruct;

#[derive(Debug, PartialEq, Clone, Hash, BoltStruct)]
#[signature(0xB2, 0x54)]
pub struct BoltTime {
    nanoseconds: BoltInteger,
    tz_offset_seconds: BoltInteger,
}

#[derive(Debug, PartialEq, Clone, Hash, BoltStruct)]
#[signature(0xB1, 0x74)]
pub struct BoltLocalTime {
    nanoseconds: BoltInteger,
}

impl Into<BoltTime> for (NaiveTime, FixedOffset) {
    fn into(self) -> BoltTime {
        let seconds_from_midnight = self.0.num_seconds_from_midnight() as i64;
        let nanoseconds = seconds_from_midnight * 1_000_000_000 + self.0.nanosecond() as i64;
        BoltTime {
            nanoseconds: nanoseconds.into(),
            tz_offset_seconds: self.1.fix().local_minus_utc().into(),
        }
    }
}

impl Into<(NaiveTime, FixedOffset)> for BoltTime {
    fn into(self) -> (NaiveTime, FixedOffset) {
        let nanos = self.nanoseconds.value;
        let seconds = (nanos / 1_000_000_000) as u32;
        let nanoseconds = (nanos % 1_000_000_000) as u32;
        (
            NaiveTime::from_num_seconds_from_midnight(seconds, nanoseconds),
            FixedOffset::east(self.tz_offset_seconds.value as i32),
        )
    }
}

impl Into<BoltLocalTime> for NaiveTime {
    fn into(self) -> BoltLocalTime {
        let seconds_from_midnight = self.num_seconds_from_midnight() as i64;
        let nanoseconds = seconds_from_midnight * 1_000_000_000 + self.nanosecond() as i64;
        BoltLocalTime {
            nanoseconds: nanoseconds.into(),
        }
    }
}

impl Into<NaiveTime> for BoltLocalTime {
    fn into(self) -> NaiveTime {
        let nanos = self.nanoseconds.value;
        let seconds = (nanos / 1_000_000_000) as u32;
        let nanoseconds = (nanos % 1_000_000_000) as u32;
        NaiveTime::from_num_seconds_from_midnight(seconds, nanoseconds)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::version::Version;
    use bytes::*;
    use std::cell::RefCell;
    use std::rc::Rc;

    #[test]
    fn should_serialize_time() {
        let time = NaiveTime::from_hms_nano_opt(7, 8, 9, 100).unwrap();
        let offset = FixedOffset::east(2 * 3600);

        let time: BoltTime = (time, offset).into();

        assert_eq!(
            time.to_bytes(Version::V4_1).unwrap(),
            Bytes::from_static(&[
                0xB2, 0x54, 0xCB, 0x00, 0x00, 0x17, 0x5D, 0x2F, 0xB8, 0x3A, 0x64, 0xC9, 0x1C, 0x20,
            ])
        );
    }

    #[test]
    fn should_deserialize_time() {
        let bytes = Rc::new(RefCell::new(Bytes::from_static(&[
            0xB2, 0x54, 0xCB, 0x00, 0x00, 0x17, 0x5D, 0x2F, 0xB8, 0x3A, 0x64, 0xC9, 0x1C, 0x20,
        ])));

        let (time, offset) = BoltTime::parse(Version::V4_1, bytes)
            .unwrap()
            .try_into()
            .unwrap();

        assert_eq!(time.to_string(), "07:08:09.000000100");
        assert_eq!(offset, FixedOffset::east(2 * 3600));
    }

    #[test]
    fn should_serialize_local_time() {
        let naive_time = NaiveTime::from_hms_nano_opt(7, 8, 9, 100).unwrap();

        let time: BoltLocalTime = naive_time.into();

        assert_eq!(
            time.to_bytes(Version::V4_1).unwrap(),
            Bytes::from_static(&[
                0xB1, 0x74, 0xCB, 0x00, 0x00, 0x17, 0x5D, 0x2F, 0xB8, 0x3A, 0x64,
            ])
        );
    }

    #[test]
    fn should_deserialize_local_time() {
        let bytes = Rc::new(RefCell::new(Bytes::from_static(&[
            0xB1, 0x74, 0xCB, 0x00, 0x00, 0x17, 0x5D, 0x2F, 0xB8, 0x3A, 0x64,
        ])));

        let time: NaiveTime = BoltLocalTime::parse(Version::V4_1, bytes)
            .unwrap()
            .try_into()
            .unwrap();

        assert_eq!(time.to_string(), "07:08:09.000000100");
    }
}