1use chrono::{FixedOffset, DateTime, Datelike, TimeZone};
8use percent_encoding::{utf8_percent_encode, AsciiSet, CONTROLS};
9use crate::error::Error;
10use crate::parameter::data::PeriodFormat;
11
12
13const PERCENT_ENCODING_FRAGMENT: &AsciiSet = &CONTROLS.add(b':').add(b'+');
14
15
16pub fn datetime_to_ecb_period<Tz>(datetime: &DateTime<Tz>, period: PeriodFormat) -> String
38where
39 Tz: TimeZone,
40 <Tz as TimeZone>::Offset: std::fmt::Display,
41{
42 let chrono_format: String = match period {
43 PeriodFormat::Annual => "%Y".to_owned(),
44 PeriodFormat::SemiAnnual => {
45 let half_year: usize = if datetime.month() <=6 { 1 } else { 2 };
46 format!("%Y-S{half_year}")
47 },
48 PeriodFormat::Quarterly => "%Y-Q%q".to_owned(),
49 PeriodFormat::Monthly => "%Y-%m".to_owned(),
50 PeriodFormat::Weekly => "%Y-W%U".to_owned(),
51 PeriodFormat::Daily => "%Y-%m-%d".to_owned(),
52 };
53 format!("{}", datetime.format(&chrono_format))
54}
55
56
57pub fn ecb_string_to_datetime(ecb_datetime: &str) -> Result<DateTime<FixedOffset>, Error> {
79 Ok(DateTime::parse_from_rfc3339(ecb_datetime)?)
80}
81
82
83pub fn percent_encode_datetime<Tz: TimeZone>(datetime: &DateTime<Tz>) -> String {
106 let datetime_str: String = datetime.to_rfc3339();
107 utf8_percent_encode(&datetime_str, PERCENT_ENCODING_FRAGMENT).to_string()
108}
109
110
111#[cfg(test)]
112mod tests {
113 use chrono::{DateTime, Datelike, FixedOffset, TimeZone, Timelike};
114
115 const HOUR: i32 = 3600;
117
118 #[test]
119 fn unit_test_datetime_to_ecb_period() -> () {
120 use crate::parameter::data::PeriodFormat;
121 use crate::time::datetime_to_ecb_period;
122 let datetime: DateTime<FixedOffset> = FixedOffset::east_opt(1 * HOUR).unwrap()
123 .with_ymd_and_hms(2009, 05, 15, 14, 15, 0).unwrap();
124 assert_eq!(datetime_to_ecb_period(&datetime, PeriodFormat::Annual), String::from("2009"));
125 assert_eq!(datetime_to_ecb_period(&datetime, PeriodFormat::SemiAnnual), String::from("2009-S1"));
126 assert_eq!(datetime_to_ecb_period(&datetime, PeriodFormat::Quarterly), String::from("2009-Q2"));
127 assert_eq!(datetime_to_ecb_period(&datetime, PeriodFormat::Monthly), String::from("2009-05"));
128 assert_eq!(datetime_to_ecb_period(&datetime, PeriodFormat::Weekly), String::from("2009-W19"));
129 assert_eq!(datetime_to_ecb_period(&datetime, PeriodFormat::Daily), String::from("2009-05-15"));
130 }
131
132 #[test]
133 fn unit_test_ecb_string_to_datetime() -> () {
134 use crate::time::ecb_string_to_datetime;
135 let datetime_str: String = String::from("2025-03-12T23:59:59.999+01:00");
136 let datetime: DateTime<FixedOffset> = ecb_string_to_datetime(&datetime_str).unwrap();
137 assert_eq!(datetime.year(), 2025);
138 assert_eq!(datetime.month(), 3);
139 assert_eq!(datetime.day(), 12);
140 assert_eq!(datetime.hour(), 23);
141 assert_eq!(datetime.minute(), 59);
142 assert_eq!(datetime.second(), 59);
143 assert_eq!(datetime.timezone(), FixedOffset::east_opt(1 * HOUR).unwrap())
144 }
145
146 #[test]
147 fn unit_test_percent_encode_datetime() -> () {
148 use crate::time::percent_encode_datetime;
149 let datetime: DateTime<FixedOffset> = FixedOffset::east_opt(1 * HOUR).unwrap()
151 .with_ymd_and_hms(2009, 05, 15, 14, 15, 0).unwrap();
152 let encoded_datetime: String = String::from("2009-05-15T14%3A15%3A00%2B01%3A00");
153 assert_eq!(percent_encode_datetime(&datetime), encoded_datetime);
154 let datetime: DateTime<FixedOffset> = FixedOffset::west_opt(1 * HOUR).unwrap()
156 .with_ymd_and_hms(2009, 05, 15, 14, 15, 0).unwrap();
157 let encoded_datetime: String = String::from("2009-05-15T14%3A15%3A00-01%3A00");
158 assert_eq!(percent_encode_datetime(&datetime), encoded_datetime);
159 }
160}