cftime_rs/
encoder.rs

1//! Module that implements the encode_cf method for`CFDatetime` and `Vec<CFDatetime>`.
2
3use crate::{
4    calendars::Calendar,
5    datetime::CFDatetime,
6    utils::{get_datetime_and_unit_from_units, unit_to_encode},
7};
8
9/// This trait represents a CFEncoder.
10/// A CFEncoder is responsible for encoding [CFDatetime] into a specific format.
11pub trait CFEncoder<T> {
12    /// Encodes the data into a specific format.
13    ///
14    /// # Arguments
15    ///
16    /// * `units` - The units of the data.
17    /// * `calendar` - The calendar to use.
18    ///
19    /// # Returns
20    ///
21    /// The encoded data as a Result<T, crate::errors::Error>.
22    fn encode_cf(&self, units: &str, calendar: Calendar) -> Result<T, crate::errors::Error>;
23}
24
25macro_rules! impl_cf_encoder {
26    ($type:ty) => {
27        impl CFEncoder<$type> for CFDatetime {
28            fn encode_cf(
29                &self,
30                units: &str,
31                calendar: Calendar,
32            ) -> Result<$type, crate::errors::Error> {
33                let (cf_datetime, unit) = get_datetime_and_unit_from_units(units, calendar)?;
34                let duration = (self - cf_datetime)?;
35                let result = unit_to_encode(&unit, duration);
36                Ok(result as $type)
37            }
38        }
39    };
40}
41
42impl_cf_encoder!(i64);
43impl_cf_encoder!(i32);
44impl_cf_encoder!(f32);
45impl_cf_encoder!(f64);
46
47macro_rules! impl_vec_cf_encoder {
48    ($type:ty) => {
49        impl CFEncoder<Vec<$type>> for Vec<CFDatetime> {
50            fn encode_cf(
51                &self,
52                units: &str,
53                calendar: Calendar,
54            ) -> Result<Vec<$type>, crate::errors::Error> {
55                let (cf_datetime, unit) = get_datetime_and_unit_from_units(units, calendar)?;
56                let mut result: Vec<$type> = Vec::with_capacity(self.len());
57                for datetime in self {
58                    let duration = (datetime - &cf_datetime)?;
59                    result.push(unit_to_encode(&unit, duration) as $type);
60                }
61                Ok(result)
62            }
63        }
64    };
65}
66
67impl_vec_cf_encoder!(i64);
68impl_vec_cf_encoder!(i32);
69impl_vec_cf_encoder!(f32);
70impl_vec_cf_encoder!(f64);
71
72macro_rules! impl_vec_ref_cf_encoder {
73    ($type:ty) => {
74        impl CFEncoder<Vec<$type>> for Vec<&CFDatetime> {
75            fn encode_cf(
76                &self,
77                units: &str,
78                calendar: Calendar,
79            ) -> Result<Vec<$type>, crate::errors::Error> {
80                let (cf_datetime, unit) = get_datetime_and_unit_from_units(units, calendar)?;
81                let mut result: Vec<$type> = Vec::with_capacity(self.len());
82                for datetime in self {
83                    let duration = (*datetime - &cf_datetime)?;
84                    result.push(unit_to_encode(&unit, duration) as $type);
85                }
86                Ok(result)
87            }
88        }
89    };
90}
91
92impl_vec_ref_cf_encoder!(i64);
93impl_vec_ref_cf_encoder!(i32);
94impl_vec_ref_cf_encoder!(f32);
95impl_vec_ref_cf_encoder!(f64);
96
97#[cfg(test)]
98mod tests {
99    use super::*;
100
101    #[test]
102    fn test_encode_cf() {
103        let datetime = CFDatetime::from_ymd(2000, 1, 1, Calendar::Standard).unwrap();
104        let result: i64 = datetime
105            .encode_cf("seconds since 2000-01-01 00:00:00", Calendar::Standard)
106            .unwrap();
107        assert_eq!(result, 0);
108        let datetime = CFDatetime::from_ymd(2023, 1, 1, Calendar::Standard).unwrap();
109        let result: i64 = datetime
110            .encode_cf("seconds since 1970-01-01 00:00:00", Calendar::Standard)
111            .unwrap();
112        assert_eq!(result, 1672531200);
113    }
114    #[test]
115    fn test_vec_encode_cf() {
116        let datetimes = vec![
117            CFDatetime::from_ymd(2000, 1, 1, Calendar::Standard).unwrap(),
118            CFDatetime::from_ymd(2000, 1, 2, Calendar::Standard).unwrap(),
119            CFDatetime::from_ymd(2000, 1, 3, Calendar::Standard).unwrap(),
120        ];
121        let result: Vec<i64> = datetimes
122            .encode_cf("seconds since 2000-01-01 00:00:00", Calendar::Standard)
123            .unwrap();
124        assert_eq!(result, vec![0, 86400, 172800]);
125    }
126    #[test]
127    fn test_vec_encode_cf_days() {
128        let units = "days since 0000-01-01 00:00:00";
129        let datetimes = vec![
130            CFDatetime::from_ymd_hms(2000, 1, 1, 0, 0, 0.0, Calendar::Standard).unwrap(),
131            CFDatetime::from_ymd_hms(2000, 1, 2, 1, 0, 0.0, Calendar::Standard).unwrap(),
132            CFDatetime::from_ymd_hms(2000, 1, 3, 2, 0, 0.0, Calendar::Standard).unwrap(),
133        ];
134        let result: Vec<f64> = datetimes.encode_cf(units, Calendar::Standard).unwrap();
135        assert_eq!(result, vec![730487.0, 730488.0416666666, 730489.0833333334]);
136    }
137}