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
use chrono::{FixedOffset, NaiveTime, Offset, Timelike};

use bolt_proto_derive::*;

pub(crate) const MARKER: u8 = 0xB2;
pub(crate) const SIGNATURE: u8 = 0x54;

#[derive(Debug, Clone, Hash, Eq, PartialEq, Signature, Marker, Serialize, Deserialize)]
pub struct Time {
    pub(crate) nanos_since_midnight: i64,
    pub(crate) zone_offset: i32,
}

impl Time {
    pub fn naive_time(&self) -> NaiveTime {
        let seconds = (self.nanos_since_midnight / 1_000_000_000) as u32;
        let nanos = (self.nanos_since_midnight % 1_000_000_000) as u32;
        // Does not panic since seconds and nanos came from a NaiveTime already
        NaiveTime::from_num_seconds_from_midnight(seconds, nanos)
    }

    pub fn offset(&self) -> FixedOffset {
        FixedOffset::east(self.zone_offset)
    }
}

// No timezone-aware time in chrono, so provide separate conversion instead
impl<O: Offset> From<(NaiveTime, O)> for Time {
    fn from(pair: (NaiveTime, O)) -> Self {
        Self {
            // Will not overflow: u32::MAX * 1_000_000_000 + u32::MAX < i64::MAX
            nanos_since_midnight: pair.0.num_seconds_from_midnight() as i64 * 1_000_000_000
                + pair.0.nanosecond() as i64,
            zone_offset: pair.1.fix().local_minus_utc(),
        }
    }
}

#[cfg(test)]
mod tests {
    use std::convert::TryFrom;
    use std::sync::{Arc, Mutex};

    use bytes::Bytes;

    use crate::serialization::*;
    use crate::value::integer::{MARKER_INT_16, MARKER_INT_64};

    use super::*;

    fn get_time() -> Time {
        Time::from((
            NaiveTime::from_hms_nano(1, 16, 40, 123),
            FixedOffset::east(3600),
        ))
    }

    #[test]
    fn get_marker() {
        assert_eq!(get_time().get_marker().unwrap(), MARKER);
    }

    #[test]
    fn try_into_bytes() {
        let time = get_time();
        assert_eq!(
            time.try_into_bytes().unwrap(),
            Bytes::from_static(&[
                MARKER,
                SIGNATURE,
                MARKER_INT_64,
                0x00,
                0x00,
                0x04,
                0x2F,
                0x05,
                0x5D,
                0xB0,
                0x7B,
                MARKER_INT_16,
                0x0E,
                0x10,
            ])
        );
    }

    #[test]
    fn try_from_bytes() {
        let time = get_time();
        let time_bytes = &[
            MARKER_INT_64,
            0x00,
            0x00,
            0x04,
            0x2F,
            0x05,
            0x5D,
            0xB0,
            0x7B,
            MARKER_INT_16,
            0x0E,
            0x10,
        ];
        assert_eq!(
            Time::try_from(Arc::new(Mutex::new(Bytes::from_static(time_bytes)))).unwrap(),
            time
        );
    }

    #[test]
    fn accessors() {
        let time = get_time();
        assert_eq!(time.naive_time(), NaiveTime::from_hms_nano(1, 16, 40, 123));
        assert_eq!(time.offset(), FixedOffset::east(3600));
    }
}