use crate::errors::*;
use crate::FirestoreResult;
use crate::{FirestoreDuration, FirestoreInstant};
pub fn from_timestamp(ts: gcloud_sdk::prost_types::Timestamp) -> FirestoreResult<FirestoreInstant> {
FirestoreInstant::new(ts.seconds, ts.nanos).map_err(|err| {
FirestoreError::DeserializeError(FirestoreSerializationError::from_message(format!(
"Invalid or out-of-range datetime: {ts:?}. {err}"
)))
})
}
pub fn to_timestamp(ts: FirestoreInstant) -> gcloud_sdk::prost_types::Timestamp {
let seconds = ts.as_second();
let nanos = ts.subsec_nanosecond();
if nanos < 0 {
gcloud_sdk::prost_types::Timestamp {
seconds: seconds - 1,
nanos: nanos + 1_000_000_000,
}
} else {
gcloud_sdk::prost_types::Timestamp { seconds, nanos }
}
}
pub fn from_duration(duration: gcloud_sdk::prost_types::Duration) -> FirestoreDuration {
FirestoreDuration::new(duration.seconds, duration.nanos)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parses_legacy_offset_formats() {
let with_offset: FirestoreInstant = "2022-12-02T16:53:20+00:00".parse().unwrap();
let with_zulu: FirestoreInstant = "2022-12-02T16:53:20Z".parse().unwrap();
assert_eq!(with_offset, with_zulu);
let fractional: FirestoreInstant = "2022-12-02T16:53:20.123456789+00:00".parse().unwrap();
assert_eq!(fractional.subsec_nanosecond(), 123_456_789);
let shifted: FirestoreInstant = "2022-12-02T17:53:20+01:00".parse().unwrap();
assert_eq!(shifted, with_zulu);
}
#[test]
fn test_timestamp_roundtrip_including_pre_epoch() {
for seconds in [0i64, 1670000000, -1670000000] {
let proto = gcloud_sdk::prost_types::Timestamp {
seconds,
nanos: 123_456_789,
};
let ts = from_timestamp(proto).unwrap();
let back = to_timestamp(ts);
assert_eq!(back.seconds, proto.seconds, "seconds for {seconds}");
assert_eq!(back.nanos, proto.nanos, "nanos for {seconds}");
}
}
#[test]
fn test_firestore_timestamp_range_bounds() {
let earliest = gcloud_sdk::prost_types::Timestamp {
seconds: -62_135_596_800,
nanos: 0,
};
let latest_supported = gcloud_sdk::prost_types::Timestamp {
seconds: FirestoreInstant::MAX.as_second(),
nanos: 0,
};
let out_of_range = gcloud_sdk::prost_types::Timestamp {
seconds: 253_402_300_799,
nanos: 999_999_999,
};
assert!(from_timestamp(earliest).is_ok(), "0001-01-01 must parse");
assert!(from_timestamp(latest_supported).is_ok());
assert!(from_timestamp(out_of_range).is_err());
}
}