1 use std::convert::TryInto; 2 3 use chrono::FixedOffset; 4 use liquid_core::Expression; 5 use liquid_core::Result; 6 use liquid_core::Runtime; 7 use liquid_core::{ 8 Display_filter, Filter, FilterParameters, FilterReflection, FromFilterParameters, ParseFilter, 9 }; 10 use liquid_core::{Value, ValueView}; 11 12 use crate::invalid_input; 13 14 // liquid-rust proprietary 15 16 #[derive(Debug, FilterParameters)] 17 struct DateInTzArgs { 18 #[parameter(description = "The format to return the date in.", arg_type = "str")] 19 format: Expression, 20 #[parameter( 21 description = "The timezone to convert the date to.", 22 arg_type = "integer" 23 )] 24 timezone: Expression, 25 } 26 27 #[derive(Clone, ParseFilter, FilterReflection)] 28 #[filter( 29 name = "date_in_tz", 30 description = "Converts a timestamp into another date format and timezone.", 31 parameters(DateInTzArgs), 32 parsed(DateInTzFilter) 33 )] 34 pub struct DateInTz; 35 36 #[derive(Debug, FromFilterParameters, Display_filter)] 37 #[name = "date_in_tz"] 38 struct DateInTzFilter { 39 #[parameters] 40 args: DateInTzArgs, 41 } 42 43 impl Filter for DateInTzFilter { evaluate(&self, input: &dyn ValueView, runtime: &dyn Runtime) -> Result<Value>44 fn evaluate(&self, input: &dyn ValueView, runtime: &dyn Runtime) -> Result<Value> { 45 let args = self.args.evaluate(runtime)?; 46 47 let date = input 48 .as_scalar() 49 .and_then(|s| s.to_date_time()) 50 .ok_or_else(|| invalid_input("Invalid date format"))?; 51 52 let timezone = FixedOffset::east( 53 (args.timezone * 3600) 54 .try_into() 55 .map_err(|_| invalid_input("Timezone was too large"))?, 56 ); 57 58 let formatter = date.with_timezone(&timezone).format(args.format.as_str()); 59 let date = formatter.to_string(); 60 Ok(Value::scalar(date)) 61 } 62 } 63 64 #[cfg(test)] 65 mod tests { 66 use super::*; 67 68 #[test] unit_date_in_tz_same_day()69 fn unit_date_in_tz_same_day() { 70 let unit_result = liquid_core::call_filter!( 71 DateInTz, 72 "13 Jun 2016 12:00:00 +0000", 73 "%Y-%m-%d %H:%M:%S %z", 74 3i64 75 ) 76 .unwrap(); 77 let desired_result = liquid_core::value!("2016-06-13 15:00:00 +0300"); 78 assert_eq!(unit_result, desired_result); 79 } 80 81 #[test] unit_date_in_tz_previous_day()82 fn unit_date_in_tz_previous_day() { 83 let unit_result = liquid_core::call_filter!( 84 DateInTz, 85 "13 Jun 2016 12:00:00 +0000", 86 "%Y-%m-%d %H:%M:%S %z", 87 -13i64 88 ) 89 .unwrap(); 90 let desired_result = liquid_core::value!("2016-06-12 23:00:00 -1300"); 91 assert_eq!(unit_result, desired_result); 92 } 93 94 #[test] unit_date_in_tz_next_day()95 fn unit_date_in_tz_next_day() { 96 let unit_result = liquid_core::call_filter!( 97 DateInTz, 98 "13 Jun 2016 12:00:00 +0000", 99 "%Y-%m-%d %H:%M:%S %z", 100 13i64 101 ) 102 .unwrap(); 103 let desired_result = liquid_core::value!("2016-06-14 01:00:00 +1300"); 104 assert_eq!(unit_result, desired_result); 105 } 106 107 #[test] unit_date_in_tz_input_not_a_string()108 fn unit_date_in_tz_input_not_a_string() { 109 liquid_core::call_filter!(DateInTz, 0f64, "%Y-%m-%d %H:%M:%S %z", 0i64).unwrap_err(); 110 } 111 112 #[test] unit_date_in_tz_input_not_a_date_string()113 fn unit_date_in_tz_input_not_a_date_string() { 114 liquid_core::call_filter!(DateInTz, "blah blah blah", "%Y-%m-%d %H:%M:%S %z", 0i64) 115 .unwrap_err(); 116 } 117 118 #[test] unit_date_in_tz_offset_not_a_num()119 fn unit_date_in_tz_offset_not_a_num() { 120 liquid_core::call_filter!( 121 DateInTz, 122 "13 Jun 2016 12:00:00 +0000", 123 "%Y-%m-%d %H:%M:%S %z", 124 "Hello" 125 ) 126 .unwrap_err(); 127 } 128 129 #[test] unit_date_in_tz_zero_arguments()130 fn unit_date_in_tz_zero_arguments() { 131 liquid_core::call_filter!(DateInTz, "13 Jun 2016 12:00:00 +0000").unwrap_err(); 132 } 133 134 #[test] unit_date_in_tz_one_argument()135 fn unit_date_in_tz_one_argument() { 136 liquid_core::call_filter!( 137 DateInTz, 138 "13 Jun 2016 12:00:00 +0000", 139 "%Y-%m-%d %H:%M:%S %z" 140 ) 141 .unwrap_err(); 142 } 143 144 #[test] unit_date_in_tz_three_arguments()145 fn unit_date_in_tz_three_arguments() { 146 liquid_core::call_filter!( 147 DateInTz, 148 "13 Jun 2016 12:00:00 +0000", 149 "%Y-%m-%d %H:%M:%S %z", 150 0f64, 151 1f64 152 ) 153 .unwrap_err(); 154 } 155 } 156