use super::BorshSerializeAs; use crate::adapters::BorshDeserializeAs; use chrono::{DateTime, Utc}; use near_sdk::borsh::{BorshDeserialize, BorshSerialize}; use std::{fmt::Display, io, marker::PhantomData}; pub struct TimestampSeconds(PhantomData); impl BorshSerializeAs> for TimestampSeconds where I: TryFrom + BorshSerialize, I::Error: Display, { #[inline] fn serialize_as(source: &DateTime, writer: &mut W) -> io::Result<()> where W: io::Write, { I::try_from(source.timestamp()) .map_err(|err| io::Error::other(err.to_string()))? .serialize(writer) } } impl BorshDeserializeAs> for TimestampSeconds where I: TryInto + BorshDeserialize, I::Error: Display, { fn deserialize_as(reader: &mut R) -> io::Result> where R: io::Read, { let timestamp = I::deserialize_reader(reader)? .try_into() .map_err(|err| io::Error::other(err.to_string()))?; DateTime::::from_timestamp(timestamp, 0) .ok_or_else(|| io::Error::other("timestamp: out of range")) } } pub struct TimestampMilliSeconds(PhantomData); impl BorshSerializeAs> for TimestampMilliSeconds where I: TryFrom + BorshSerialize, I::Error: Display, { #[inline] fn serialize_as(source: &DateTime, writer: &mut W) -> io::Result<()> where W: io::Write, { I::try_from(source.timestamp_millis()) .map_err(|err| io::Error::other(err.to_string()))? .serialize(writer) } } impl BorshDeserializeAs> for TimestampMilliSeconds where I: TryInto + BorshDeserialize, I::Error: Display, { fn deserialize_as(reader: &mut R) -> io::Result> where R: io::Read, { let timestamp = I::deserialize_reader(reader)? .try_into() .map_err(|err| io::Error::other(err.to_string()))?; DateTime::::from_timestamp_millis(timestamp) .ok_or_else(|| io::Error::other("timestamp: out of range")) } } pub struct TimestampMicroSeconds(PhantomData); impl BorshSerializeAs> for TimestampMicroSeconds where I: TryFrom + BorshSerialize, I::Error: Display, { #[inline] fn serialize_as(source: &DateTime, writer: &mut W) -> io::Result<()> where W: io::Write, { I::try_from(source.timestamp_micros()) .map_err(|err| io::Error::other(err.to_string()))? .serialize(writer) } } impl BorshDeserializeAs> for TimestampMicroSeconds where I: TryInto + BorshDeserialize, I::Error: Display, { fn deserialize_as(reader: &mut R) -> io::Result> where R: io::Read, { let timestamp = I::deserialize_reader(reader)? .try_into() .map_err(|err| io::Error::other(err.to_string()))?; DateTime::::from_timestamp_micros(timestamp) .ok_or_else(|| io::Error::other("timestamp: out of range")) } } pub struct TimestampNanoSeconds(PhantomData); impl BorshSerializeAs> for TimestampNanoSeconds where I: TryFrom + BorshSerialize, I::Error: Display, { #[inline] fn serialize_as(source: &DateTime, writer: &mut W) -> io::Result<()> where W: io::Write, { I::try_from( source .timestamp_nanos_opt() .ok_or_else(|| io::Error::other("timestamp: out of range"))?, ) .map_err(|err| io::Error::other(err.to_string()))? .serialize(writer) } } impl BorshDeserializeAs> for TimestampNanoSeconds where I: TryInto + BorshDeserialize, I::Error: Display, { fn deserialize_as(reader: &mut R) -> io::Result> where R: io::Read, { let timestamp = I::deserialize_reader(reader)? .try_into() .map_err(|err| io::Error::other(err.to_string()))?; Ok(DateTime::::from_timestamp_nanos(timestamp)) } } #[cfg(test)] mod tests { use crate::adapters::tests::roundtrip_as; use super::*; use chrono::{DateTime, TimeZone, Utc}; #[test] fn timestamp_seconds_i64_roundtrip() { roundtrip_as::<_, TimestampSeconds>(&Utc.timestamp_opt(1_600_000_000, 0).unwrap()); } #[test] fn timestamp_milliseconds_i64_roundtrip() { roundtrip_as::<_, TimestampMilliSeconds>( &DateTime::::from_timestamp_millis(1_600_000_000_123).unwrap(), ); } #[test] fn timestamp_microseconds_i64_roundtrip() { roundtrip_as::<_, TimestampMicroSeconds>( &DateTime::::from_timestamp_micros(1_600_000_000_123_456).unwrap(), ); } #[test] fn timestamp_nanoseconds_i64_roundtrip() { roundtrip_as::<_, TimestampNanoSeconds>(&DateTime::::from_timestamp_nanos( 1_600_000_000_123_456_789, )); } }