cftime_rs/
decoder.rs

1//! Module that implements the decode_cf method for `i32`, `i64`, `f32`, `f64`,  `Vec<i32>`, `Vec<i64>`, `Vec<f32>` and `Vec<f64>`.
2
3use crate::utils::get_datetime_and_unit_from_units;
4use crate::{calendars::Calendar, datetime::CFDatetime};
5
6/// Trait for decoding CFDatetime from units and calendar
7pub trait CFDecoder {
8    /// Decodes the given units and calendar into a CFDatetime.
9    ///
10    /// # Arguments
11    ///
12    /// * `units` - The units to decode.
13    /// * `calendar` - The calendar to use for decoding.
14    ///
15    /// # Returns
16    ///
17    /// A Result containing the decoded CFDatetime if successful, or an Error if decoding fails.
18    fn decode_cf(
19        &self,
20        units: &str,
21        calendar: Calendar,
22    ) -> Result<CFDatetime, crate::errors::Error>;
23}
24
25macro_rules! impl_cf_decoder {
26    ($type:ty) => {
27        impl CFDecoder for $type {
28            fn decode_cf(
29                &self,
30                units: &str,
31                calendar: Calendar,
32            ) -> Result<CFDatetime, crate::errors::Error> {
33                let (cf_datetime, unit) = get_datetime_and_unit_from_units(units, calendar)?;
34                let duration = unit.to_duration(calendar);
35                let result = (&cf_datetime + (&duration * *self))?;
36
37                Ok(result)
38            }
39        }
40    };
41}
42
43impl_cf_decoder!(i64);
44impl_cf_decoder!(i32);
45impl_cf_decoder!(f32);
46impl_cf_decoder!(f64);
47
48pub trait VecCFDecoder {
49    fn decode_cf(
50        &self,
51        units: &str,
52        calendar: Calendar,
53    ) -> Result<Vec<CFDatetime>, crate::errors::Error>;
54}
55
56macro_rules! impl_vec_cf_decoder {
57    ($type:ty) => {
58        impl VecCFDecoder for Vec<$type> {
59            fn decode_cf(
60                &self,
61                units: &str,
62                calendar: Calendar,
63            ) -> Result<Vec<CFDatetime>, crate::errors::Error> {
64                let (cf_datetime, unit) = get_datetime_and_unit_from_units(units, calendar)?;
65                let duration = unit.to_duration(calendar);
66                let mut datetimes = Vec::with_capacity(self.len());
67                for value in self {
68                    let new_datetime = &cf_datetime + (&duration * *value);
69                    datetimes.push(new_datetime?);
70                }
71
72                Ok(datetimes)
73            }
74        }
75    };
76}
77
78impl_vec_cf_decoder!(i64);
79impl_vec_cf_decoder!(i32);
80impl_vec_cf_decoder!(f32);
81impl_vec_cf_decoder!(f64);
82
83#[cfg(test)]
84mod tests {
85    use super::*;
86
87    #[test]
88    fn test_decode_i64_cf_with_hms() {
89        let to_decode = 2;
90        let units = "days since 2000-01-01 00:00:00";
91        let calendar = Calendar::Standard;
92
93        let result = to_decode.decode_cf(units, calendar);
94
95        // Assert
96        assert!(result.is_ok());
97        let cf_datetime = result.unwrap();
98        let (year, month, day, hour, minute, second) = cf_datetime.ymd_hms().unwrap();
99        assert_eq!(
100            (year, month, day, hour, minute, second),
101            (2000, 1, 3, 0, 0, 0)
102        );
103    }
104    #[test]
105    fn test_decode_f64_cf_with_hms() {
106        let to_decode: f64 = 2.0;
107        let units = "hours since 2000-01-01 00:00:00";
108        let calendar = Calendar::Standard;
109
110        let result = to_decode.decode_cf(units, calendar);
111
112        // Assert
113        assert!(result.is_ok());
114        let cf_datetime = result.unwrap();
115        let (year, month, day, hour, minute, second) = cf_datetime.ymd_hms().unwrap();
116        assert_eq!(
117            (year, month, day, hour, minute, second),
118            (2000, 1, 1, 2, 0, 0)
119        );
120    }
121
122    #[test]
123    fn test_decode_i32_vec_cf_with_hms() {
124        let to_decode = vec![0, 1, 2];
125        let units = "days since 2000-01-01 00:00:00";
126        let calendar = Calendar::Standard;
127
128        let datetimes_result = to_decode.decode_cf(units, calendar);
129
130        // Assert
131        assert!(datetimes_result.is_ok());
132        let datetimes = datetimes_result.unwrap();
133        for (i, datetime) in datetimes.iter().enumerate() {
134            let (year, month, day, hour, minute, second) = datetime.ymd_hms().unwrap();
135            assert_eq!(
136                (year, month, day, hour, minute, second),
137                (2000, 1, (i + 1) as u8, 0, 0, 0)
138            );
139        }
140    }
141    #[test]
142    fn test_decode_f64_vec_cf_with_hms() {
143        let to_decode: Vec<f64> = vec![1.0, 1.25, 1.50, 1.75, 2.0];
144        let units = "hours since 2000-01-01 00:00:00";
145        let calendar = Calendar::Standard;
146
147        let datetimes_result = to_decode.decode_cf(units, calendar);
148
149        // Assert
150        assert!(datetimes_result.is_ok());
151        let datetimes = datetimes_result.unwrap();
152        let (year, month, day, hour, minute, second) = datetimes[0].ymd_hms().unwrap();
153        assert_eq!(
154            (year, month, day, hour, minute, second),
155            (2000, 1, 1, 1, 0, 0)
156        );
157        let (year, month, day, hour, minute, second) = datetimes[1].ymd_hms().unwrap();
158        assert_eq!(
159            (year, month, day, hour, minute, second),
160            (2000, 1, 1, 1, 15, 0)
161        );
162        let (year, month, day, hour, minute, second) = datetimes[2].ymd_hms().unwrap();
163        assert_eq!(
164            (year, month, day, hour, minute, second),
165            (2000, 1, 1, 1, 30, 0)
166        );
167        let (year, month, day, hour, minute, second) = datetimes[3].ymd_hms().unwrap();
168        assert_eq!(
169            (year, month, day, hour, minute, second),
170            (2000, 1, 1, 1, 45, 0)
171        );
172        let (year, month, day, hour, minute, second) = datetimes[4].ymd_hms().unwrap();
173        assert_eq!(
174            (year, month, day, hour, minute, second),
175            (2000, 1, 1, 2, 0, 0)
176        );
177    }
178    #[test]
179    fn test_decode_95795_from_days() {
180        // This error originates from Python bindings
181        // From 95795.0 it gives (2232, 4, 11, 23, 57, 52) instead of (2232, 4, 12, 0, 0, 0)
182        // But this worked for int
183        let to_decode: Vec<f32> = vec![95795.0];
184        let units = "days since 1970-01-01";
185        let calendar = Calendar::Standard;
186
187        let datetimes_result = to_decode.decode_cf(units, calendar);
188        let datetimes = datetimes_result.unwrap();
189        let (year, month, day, hour, minute, second) = datetimes[0].ymd_hms().unwrap();
190        assert_eq!(
191            (year, month, day, hour, minute, second),
192            (2232, 4, 12, 0, 0, 0)
193        );
194
195        let to_decode: Vec<f64> = vec![95795.0];
196        let units = "days since 1970-01-01";
197        let calendar = Calendar::Standard;
198
199        let datetimes_result = to_decode.decode_cf(units, calendar);
200        let datetimes = datetimes_result.unwrap();
201        let (year, month, day, hour, minute, second) = datetimes[0].ymd_hms().unwrap();
202        assert_eq!(
203            (year, month, day, hour, minute, second),
204            (2232, 4, 12, 0, 0, 0)
205        );
206        let to_decode: Vec<i64> = vec![95795];
207        let units = "days since 1970-01-01";
208        let calendar = Calendar::Standard;
209
210        let datetimes_result = to_decode.decode_cf(units, calendar);
211        let datetimes = datetimes_result.unwrap();
212        let (year, month, day, hour, minute, second) = datetimes[0].ymd_hms().unwrap();
213        assert_eq!(
214            (year, month, day, hour, minute, second),
215            (2232, 4, 12, 0, 0, 0)
216        );
217    }
218    #[test]
219    fn test_vec_decode_cf_days() {
220        // Inverse function of test_vec_encode_cf_days
221        let units = "days since 0000-01-01 00:00:00";
222        // Tests with f64
223        let numbers = vec![730487.0, 730488.0416666666, 730489.0833333334];
224        let result = numbers.decode_cf(units, Calendar::Standard).unwrap();
225        let datetimes = vec![
226            CFDatetime::from_ymd_hms(2000, 1, 1, 0, 0, 0.0, Calendar::Standard).unwrap(),
227            CFDatetime::from_ymd_hms(2000, 1, 2, 1, 0, 0.0, Calendar::Standard).unwrap(),
228            CFDatetime::from_ymd_hms(2000, 1, 3, 2, 0, 0.0, Calendar::Standard).unwrap(),
229        ];
230        for (i, datetime) in datetimes.iter().enumerate() {
231            let expected_ymd_hms = datetime.ymd_hms().unwrap();
232            let result_ymd_hms = result[i].ymd_hms().unwrap();
233            println!("{}", result[i]);
234            assert_eq!(expected_ymd_hms, result_ymd_hms);
235        }
236
237        // Tests with f32 that can't work
238        // 730488.0416666666 can't be represented in f32 better than 730488.0625
239        // So we got
240        // 2000-01-01 00:00:00.000
241        // 2000-01-02 01:30:00.000
242        // 2000-01-03 01:30:00.000
243        // instead of
244        // 2000-01-01 00:00:00.000
245        // 2000-01-02 01:00:00.000
246        // 2000-01-03 02:00:00.000
247        let units = "days since 0000-01-01 00:00:00";
248        let numbers: Vec<f32> = vec![730487.0, 730488.0416666666, 730489.0833333334];
249        let result = numbers.decode_cf(units, Calendar::Standard).unwrap();
250        let datetimes = vec![
251            CFDatetime::from_ymd_hms(2000, 1, 1, 0, 0, 0.0, Calendar::Standard).unwrap(),
252            CFDatetime::from_ymd_hms(2000, 1, 2, 1, 30, 0.0, Calendar::Standard).unwrap(),
253            CFDatetime::from_ymd_hms(2000, 1, 3, 1, 30, 0.0, Calendar::Standard).unwrap(),
254        ];
255        for (i, datetime) in datetimes.iter().enumerate() {
256            println!("{}", result[i]);
257            let expected_ymd_hms = datetime.ymd_hms().unwrap();
258            let result_ymd_hms = result[i].ymd_hms().unwrap();
259            assert_eq!(expected_ymd_hms, result_ymd_hms);
260        }
261    }
262    // Add more test cases for other scenarios as needed
263}