handlebars_chrono/
datetime.rs

1#[cfg(feature = "locale")]
2use chrono::Locale;
3use chrono::{DateTime, Datelike, Days, FixedOffset, Local, Months, NaiveDateTime, TimeDelta, Timelike, Utc};
4#[cfg(feature = "timezone")]
5use chrono_tz::Tz;
6use handlebars::{Context, Handlebars, Helper, HelperDef, HelperResult, Output, RenderContext, RenderError, RenderErrorReason};
7use std::num::ParseIntError;
8use std::str::FromStr;
9
10#[derive(Clone, Copy)]
11/// Chrono DateTime helper for Handlebars
12///
13/// # Registration
14///
15/// ```rust
16/// use chrono::Utc;
17/// use handlebars::Handlebars;
18/// use handlebars_chrono::HandlebarsChronoDateTime;
19/// use serde_json::json;
20///
21/// let mut h = Handlebars::new();
22/// h.register_helper("datetime", Box::new(HandlebarsChronoDateTime));
23///
24/// assert_eq!(h.render_template(r#"{{datetime}}"#, &json!({})).map(|s| s.as_str()[..16].to_string()).expect("Render error"), Utc::now().to_rfc3339().as_str()[..16].to_string());
25/// ```
26///
27/// # Behavior
28///
29/// TODO
30///
31/// # Hash parameters
32///
33/// TODO
34///
35/// # Example usage:
36///
37///
38///
39pub struct HandlebarsChronoDateTime;
40
41impl HelperDef for HandlebarsChronoDateTime {
42    fn call<'reg: 'rc, 'rc>(
43        &self,
44        h: &Helper<'rc>,
45        _r: &'reg Handlebars,
46        _ctx: &'rc Context,
47        _rc: &mut RenderContext<'reg, 'rc>,
48        out: &mut dyn Output,
49    ) -> HelperResult {
50        // INITIALIZERS
51        //
52        // default Utc::now()
53        // from_timestamp (secs, 0)
54        // from_timestamp_millis (millis)
55        // from_timestamp_micros (micros)
56        // from_timestamp_nanos (nanos)
57        // parse_from_rfc2822
58        // parse_from_rfc3339
59        // parse_from_str + input_format
60        let datetime = if let Some(timestamp) = h.hash_get("from_timestamp") {
61            let timestamp = timestamp.render();
62
63            DateTime::from_timestamp(
64                timestamp.parse().map_err(|e: ParseIntError| {
65                    <RenderErrorReason as Into<RenderError>>::into(RenderErrorReason::Other(format!("Invalid seconds timestamp: {}", e)))
66                })?,
67                0,
68            )
69            .ok_or::<RenderError>(RenderErrorReason::Other("Out-of-range number of seconds".to_string()).into())?
70        } else if let Some(timestamp) = h.hash_get("from_timestamp_millis") {
71            let timestamp = timestamp.render();
72
73            DateTime::from_timestamp_millis(timestamp.parse().map_err(|e: ParseIntError| {
74                <RenderErrorReason as Into<RenderError>>::into(RenderErrorReason::Other(format!("Invalid milli-seconds timestamp: {}", e)))
75            })?)
76            .ok_or::<RenderError>(RenderErrorReason::Other("Out-of-range number of milliseconds".to_string()).into())?
77        } else if let Some(timestamp) = h.hash_get("from_timestamp_micros") {
78            let timestamp = timestamp.render();
79
80            DateTime::from_timestamp_micros(timestamp.parse().map_err(|e: ParseIntError| {
81                <RenderErrorReason as Into<RenderError>>::into(RenderErrorReason::Other(format!("Invalid micro-seconds timestamp: {}", e)))
82            })?)
83            .ok_or::<RenderError>(
84                RenderErrorReason::Other(
85                    "Number of microseconds would be out of range for a NaiveDateTime (more than ca. 262,000 years away from common era)"
86                        .to_string(),
87                )
88                .into(),
89            )?
90        } else if let Some(timestamp) = h.hash_get("from_timestamp_nanos") {
91            let timestamp = timestamp.render();
92
93            DateTime::from_timestamp_nanos(timestamp.parse().map_err(|e: ParseIntError| {
94                <RenderErrorReason as Into<RenderError>>::into(RenderErrorReason::Other(format!("Invalid nano-seconds timestamp: {}", e)))
95            })?)
96        } else if let Some(input_str) = h.hash_get("from_rfc2822") {
97            let input_str = input_str.render();
98
99            DateTime::parse_from_rfc2822(&input_str)
100                .map_err(|e| {
101                    <RenderErrorReason as Into<RenderError>>::into(RenderErrorReason::Other(format!(
102                        "Invalid RFC2822 datetime format: {}",
103                        e
104                    )))
105                })?
106                .to_utc()
107        } else if let Some(input_str) = h.hash_get("from_rfc3339") {
108            let input_str = input_str.render();
109
110            DateTime::parse_from_rfc3339(&input_str)
111                .map_err(|e| {
112                    <RenderErrorReason as Into<RenderError>>::into(RenderErrorReason::Other(format!(
113                        "Invalid RFC3339 datetime format: {}",
114                        e
115                    )))
116                })?
117                .to_utc()
118        } else if let Some(input_str) = h.hash_get("from_str") {
119            if let Some(input_format) = h.hash_get("input_format") {
120                let input_str = input_str.render();
121                let input_format = input_format.render();
122
123                NaiveDateTime::parse_from_str(&input_str, &input_format)
124                    .map_err(|e| {
125                        <RenderErrorReason as Into<RenderError>>::into(RenderErrorReason::Other(format!(
126                            "Invalid datetime format or format doesn't match input: {}",
127                            e
128                        )))
129                    })?
130                    .and_utc()
131            } else {
132                // error, missing input format
133                return Err(RenderErrorReason::Other("Missing `input_format` hash parameter".to_string()).into());
134            }
135        } else {
136            Utc::now()
137        };
138
139        // MODIFIERS (by default everything is converted to UTC by the initializer)
140        //
141        // with_timezone
142        // with_ordinal
143        // with_ordinal0
144        // with_year
145        // with_month
146        // with_month0
147        // with_day
148        // with_day0
149        // with_hour
150        // with_minute
151        // with_second
152        // with_nanosecond
153        // add_months
154        // add_weeks
155        // add_days
156        // add_hours
157        // add_minutes
158        // add_seconds
159        // add_milliseconds
160        // add_microseconds
161        // add_nanoseconds
162        // sub_months
163        // sub_weeks
164        // sub_days
165        // sub_hours
166        // sub_minutes
167        // sub_seconds
168        // sub_milliseconds
169        // sub_microseconds
170        // sub_nanoseconds
171        let datetime = if let Some(timezone) = h.hash_get("with_timezone") {
172            let timezone = timezone.render();
173            let tz: FixedOffset = if timezone.to_lowercase() == "local" {
174                Local::now().fixed_offset().timezone()
175            } else if timezone.contains('0') {
176                if let Ok(tz) = FixedOffset::from_str(&timezone) {
177                    tz
178                } else {
179                    return Err(RenderErrorReason::Other(
180                        "Failed to parse timezone offset. Supported values are IANA timezones, local or valid fixed offset".to_string(),
181                    )
182                    .into());
183                }
184            } else {
185                #[cfg(feature = "timezone")]
186                if let Ok(tz) = timezone.parse::<Tz>() {
187                    datetime.with_timezone(&tz).fixed_offset().timezone()
188                } else {
189                    return Err(RenderErrorReason::Other(
190                        "Failed to parse IANA timezone. Supported values are IANA timezones, local or valid fixed offset".to_string(),
191                    )
192                    .into());
193                }
194
195                #[cfg(not(feature = "timezone"))]
196                return Err(RenderErrorReason::Other(
197                    "You need to enable the `timezone` feature of the `handlebars-chrono` create for IANA timezones to work.".to_string(),
198                )
199                .into());
200            };
201
202            datetime.with_timezone(&tz)
203        } else {
204            datetime.fixed_offset()
205        };
206
207        let datetime = if let Some(day) = h.hash_get("with_ordinal") {
208            let day = day.render().parse::<u32>().map_err(|e| {
209                <RenderErrorReason as Into<RenderError>>::into(RenderErrorReason::Other(format!("Invalid ordinal parameter: {}", e)))
210            })?;
211
212            datetime
213                .with_ordinal(day)
214                .ok_or::<RenderError>(RenderErrorReason::Other("Ordinal parameter out of range".to_string()).into())?
215        } else {
216            datetime
217        };
218
219        let datetime = if let Some(day) = h.hash_get("with_ordinal0") {
220            let day = day.render().parse::<u32>().map_err(|e| {
221                <RenderErrorReason as Into<RenderError>>::into(RenderErrorReason::Other(format!("Invalid ordinal parameter: {}", e)))
222            })?;
223
224            datetime
225                .with_ordinal0(day)
226                .ok_or::<RenderError>(RenderErrorReason::Other("Ordinal parameter out of range".to_string()).into())?
227        } else {
228            datetime
229        };
230
231        let datetime = if let Some(year) = h.hash_get("with_year") {
232            let year = year.render().parse::<i32>().map_err(|e| {
233                <RenderErrorReason as Into<RenderError>>::into(RenderErrorReason::Other(format!("Invalid year parameter: {}", e)))
234            })?;
235
236            datetime
237                .with_year(year)
238                .ok_or::<RenderError>(RenderErrorReason::Other("Year parameter out of range or produces invalid date".to_string()).into())?
239        } else {
240            datetime
241        };
242
243        let datetime = if let Some(month) = h.hash_get("with_month") {
244            let month = month.render().parse::<u32>().map_err(|e| {
245                <RenderErrorReason as Into<RenderError>>::into(RenderErrorReason::Other(format!("Invalid month parameter: {}", e)))
246            })?;
247
248            datetime.with_month(month).ok_or::<RenderError>(
249                RenderErrorReason::Other("Month parameter out of range or produces invalid date".to_string()).into(),
250            )?
251        } else {
252            datetime
253        };
254
255        let datetime = if let Some(month) = h.hash_get("with_month0") {
256            let month = month.render().parse::<u32>().map_err(|e| {
257                <RenderErrorReason as Into<RenderError>>::into(RenderErrorReason::Other(format!("Invalid month parameter: {}", e)))
258            })?;
259
260            datetime.with_month0(month).ok_or::<RenderError>(
261                RenderErrorReason::Other("Month parameter out of range or produces invalid date".to_string()).into(),
262            )?
263        } else {
264            datetime
265        };
266
267        let datetime = if let Some(day) = h.hash_get("with_day") {
268            let day = day.render().parse::<u32>().map_err(|e| {
269                <RenderErrorReason as Into<RenderError>>::into(RenderErrorReason::Other(format!("Invalid day parameter: {}", e)))
270            })?;
271
272            datetime
273                .with_day(day)
274                .ok_or::<RenderError>(RenderErrorReason::Other("Day parameter out of range or produces invalid date".to_string()).into())?
275        } else {
276            datetime
277        };
278
279        let datetime = if let Some(day) = h.hash_get("with_day0") {
280            let day = day.render().parse::<u32>().map_err(|e| {
281                <RenderErrorReason as Into<RenderError>>::into(RenderErrorReason::Other(format!("Invalid day parameter: {}", e)))
282            })?;
283
284            datetime
285                .with_day0(day)
286                .ok_or::<RenderError>(RenderErrorReason::Other("Day parameter out of range or produces invalid date".to_string()).into())?
287        } else {
288            datetime
289        };
290
291        let datetime = if let Some(hour) = h.hash_get("with_hour") {
292            let hour = hour.render().parse::<u32>().map_err(|e| {
293                <RenderErrorReason as Into<RenderError>>::into(RenderErrorReason::Other(format!("Invalid hour parameter: {}", e)))
294            })?;
295
296            datetime
297                .with_hour(hour)
298                .ok_or::<RenderError>(RenderErrorReason::Other("Hour parameter out of range or produces invalid date".to_string()).into())?
299        } else {
300            datetime
301        };
302
303        let datetime = if let Some(min) = h.hash_get("with_minute") {
304            let min = min.render().parse::<u32>().map_err(|e| {
305                <RenderErrorReason as Into<RenderError>>::into(RenderErrorReason::Other(format!("Invalid minute parameter: {}", e)))
306            })?;
307
308            datetime.with_minute(min).ok_or::<RenderError>(
309                RenderErrorReason::Other("Minute parameter out of range or produces invalid date".to_string()).into(),
310            )?
311        } else {
312            datetime
313        };
314
315        let datetime = if let Some(sec) = h.hash_get("with_second") {
316            let sec = sec.render().parse::<u32>().map_err(|e| {
317                <RenderErrorReason as Into<RenderError>>::into(RenderErrorReason::Other(format!("Invalid second parameter: {}", e)))
318            })?;
319
320            datetime.with_second(sec).ok_or::<RenderError>(
321                RenderErrorReason::Other("Second parameter out of range or produces invalid date".to_string()).into(),
322            )?
323        } else {
324            datetime
325        };
326
327        let datetime = if let Some(nsec) = h.hash_get("with_nanosecond") {
328            let nsec = nsec.render().parse::<u32>().map_err(|e| {
329                <RenderErrorReason as Into<RenderError>>::into(RenderErrorReason::Other(format!("Invalid nano-second parameter: {}", e)))
330            })?;
331
332            datetime.with_nanosecond(nsec).ok_or::<RenderError>(
333                RenderErrorReason::Other("Nano-second parameter out of range or produces invalid date".to_string()).into(),
334            )?
335        } else {
336            datetime
337        };
338
339        // add_
340
341        let datetime = if let Some(months) = h.hash_get("add_months") {
342            let months = months.render().parse::<u32>().map_err(|e| {
343                <RenderErrorReason as Into<RenderError>>::into(RenderErrorReason::Other(format!("Invalid months parameter: {}", e)))
344            })?;
345
346            datetime.checked_add_months(Months::new(months)).ok_or::<RenderError>(
347                RenderErrorReason::Other("Months parameter out of range or produces invalid date".to_string()).into(),
348            )?
349        } else {
350            datetime
351        };
352
353        let datetime = if let Some(weeks) = h.hash_get("add_weeks") {
354            let weeks = weeks.render().parse::<i64>().map_err(|e| {
355                <RenderErrorReason as Into<RenderError>>::into(RenderErrorReason::Other(format!("Invalid weeks parameter: {}", e)))
356            })?;
357
358            datetime
359                .checked_add_signed(
360                    TimeDelta::try_weeks(weeks)
361                        .ok_or::<RenderError>(RenderErrorReason::Other("Weeks parameter out of range".to_string()).into())?,
362                )
363                .ok_or::<RenderError>(
364                    RenderErrorReason::Other("Weeks parameter out of range or produces invalid date".to_string()).into(),
365                )?
366        } else {
367            datetime
368        };
369
370        let datetime = if let Some(days) = h.hash_get("add_days") {
371            let days = days.render().parse::<u64>().map_err(|e| {
372                <RenderErrorReason as Into<RenderError>>::into(RenderErrorReason::Other(format!("Invalid days parameter: {}", e)))
373            })?;
374
375            datetime
376                .checked_add_days(Days::new(days))
377                .ok_or::<RenderError>(RenderErrorReason::Other("Days parameter out of range or produces invalid date".to_string()).into())?
378        } else {
379            datetime
380        };
381
382        let datetime = if let Some(hours) = h.hash_get("add_hours") {
383            let hours = hours.render().parse::<i64>().map_err(|e| {
384                <RenderErrorReason as Into<RenderError>>::into(RenderErrorReason::Other(format!("Invalid hours parameter: {}", e)))
385            })?;
386
387            datetime
388                .checked_add_signed(
389                    TimeDelta::try_hours(hours)
390                        .ok_or::<RenderError>(RenderErrorReason::Other("Hours parameter out of range".to_string()).into())?,
391                )
392                .ok_or::<RenderError>(
393                    RenderErrorReason::Other("Hours parameter out of range or produces invalid date".to_string()).into(),
394                )?
395        } else {
396            datetime
397        };
398
399        let datetime = if let Some(min) = h.hash_get("add_minutes") {
400            let min = min.render().parse::<i64>().map_err(|e| {
401                <RenderErrorReason as Into<RenderError>>::into(RenderErrorReason::Other(format!("Invalid minutes parameter: {}", e)))
402            })?;
403
404            datetime
405                .checked_add_signed(
406                    TimeDelta::try_minutes(min)
407                        .ok_or::<RenderError>(RenderErrorReason::Other("Minutes parameter out of range".to_string()).into())?,
408                )
409                .ok_or::<RenderError>(
410                    RenderErrorReason::Other("Minutes parameter out of range or produces invalid date".to_string()).into(),
411                )?
412        } else {
413            datetime
414        };
415
416        let datetime = if let Some(sec) = h.hash_get("add_seconds") {
417            let sec = sec.render().parse::<i64>().map_err(|e| {
418                <RenderErrorReason as Into<RenderError>>::into(RenderErrorReason::Other(format!("Invalid seconds parameter: {}", e)))
419            })?;
420
421            datetime
422                .checked_add_signed(
423                    TimeDelta::try_seconds(sec)
424                        .ok_or::<RenderError>(RenderErrorReason::Other("Seconds parameter out of range".to_string()).into())?,
425                )
426                .ok_or::<RenderError>(
427                    RenderErrorReason::Other("Seconds parameter out of range or produces invalid date".to_string()).into(),
428                )?
429        } else {
430            datetime
431        };
432
433        let datetime = if let Some(msec) = h.hash_get("add_milliseconds") {
434            let msec = msec.render().parse::<i64>().map_err(|e| {
435                <RenderErrorReason as Into<RenderError>>::into(RenderErrorReason::Other(format!("Invalid milli-seconds parameter: {}", e)))
436            })?;
437
438            datetime
439                .checked_add_signed(
440                    TimeDelta::try_milliseconds(msec)
441                        .ok_or::<RenderError>(RenderErrorReason::Other("Milli-seconds parameter out of range".to_string()).into())?,
442                )
443                .ok_or::<RenderError>(
444                    RenderErrorReason::Other("Milli-seconds parameter out of range or produces invalid date".to_string()).into(),
445                )?
446        } else {
447            datetime
448        };
449
450        let datetime = if let Some(usec) = h.hash_get("add_microseconds") {
451            let usec = usec.render().parse::<i64>().map_err(|e| {
452                <RenderErrorReason as Into<RenderError>>::into(RenderErrorReason::Other(format!("Invalid micro-seconds parameter: {}", e)))
453            })?;
454
455            datetime.checked_add_signed(TimeDelta::microseconds(usec)).ok_or::<RenderError>(
456                RenderErrorReason::Other("Micro-seconds parameter out of range or produces invalid date".to_string()).into(),
457            )?
458        } else {
459            datetime
460        };
461
462        let datetime = if let Some(nsec) = h.hash_get("add_nanoseconds") {
463            let nsec = nsec.render().parse::<i64>().map_err(|e| {
464                <RenderErrorReason as Into<RenderError>>::into(RenderErrorReason::Other(format!("Invalid nano-seconds parameter: {}", e)))
465            })?;
466
467            datetime.checked_add_signed(TimeDelta::nanoseconds(nsec)).ok_or::<RenderError>(
468                RenderErrorReason::Other("Nano-seconds parameter out of range or produces invalid date".to_string()).into(),
469            )?
470        } else {
471            datetime
472        };
473
474        // sub_
475
476        let datetime = if let Some(months) = h.hash_get("sub_months") {
477            let months = months.render().parse::<u32>().map_err(|e| {
478                <RenderErrorReason as Into<RenderError>>::into(RenderErrorReason::Other(format!("Invalid months parameter: {}", e)))
479            })?;
480
481            datetime.checked_sub_months(Months::new(months)).ok_or::<RenderError>(
482                RenderErrorReason::Other("Months parameter out of range or produces invalid date".to_string()).into(),
483            )?
484        } else {
485            datetime
486        };
487
488        let datetime = if let Some(weeks) = h.hash_get("sub_weeks") {
489            let weeks = weeks.render().parse::<i64>().map_err(|e| {
490                <RenderErrorReason as Into<RenderError>>::into(RenderErrorReason::Other(format!("Invalid weeks parameter: {}", e)))
491            })?;
492
493            datetime
494                .checked_sub_signed(
495                    TimeDelta::try_weeks(weeks)
496                        .ok_or::<RenderError>(RenderErrorReason::Other("Weeks parameter out of range".to_string()).into())?,
497                )
498                .ok_or::<RenderError>(
499                    RenderErrorReason::Other("Weeks parameter out of range or produces invalid date".to_string()).into(),
500                )?
501        } else {
502            datetime
503        };
504
505        let datetime = if let Some(days) = h.hash_get("sub_days") {
506            let days = days.render().parse::<u64>().map_err(|e| {
507                <RenderErrorReason as Into<RenderError>>::into(RenderErrorReason::Other(format!("Invalid days parameter: {}", e)))
508            })?;
509
510            datetime
511                .checked_sub_days(Days::new(days))
512                .ok_or::<RenderError>(RenderErrorReason::Other("Days parameter out of range or produces invalid date".to_string()).into())?
513        } else {
514            datetime
515        };
516
517        let datetime = if let Some(hours) = h.hash_get("sub_hours") {
518            let hours = hours.render().parse::<i64>().map_err(|e| {
519                <RenderErrorReason as Into<RenderError>>::into(RenderErrorReason::Other(format!("Invalid hours parameter: {}", e)))
520            })?;
521
522            datetime
523                .checked_sub_signed(
524                    TimeDelta::try_hours(hours)
525                        .ok_or::<RenderError>(RenderErrorReason::Other("Hours parameter out of range".to_string()).into())?,
526                )
527                .ok_or::<RenderError>(
528                    RenderErrorReason::Other("Hours parameter out of range or produces invalid date".to_string()).into(),
529                )?
530        } else {
531            datetime
532        };
533
534        let datetime = if let Some(min) = h.hash_get("sub_minutes") {
535            let min = min.render().parse::<i64>().map_err(|e| {
536                <RenderErrorReason as Into<RenderError>>::into(RenderErrorReason::Other(format!("Invalid minutes parameter: {}", e)))
537            })?;
538
539            datetime
540                .checked_sub_signed(
541                    TimeDelta::try_minutes(min)
542                        .ok_or::<RenderError>(RenderErrorReason::Other("Minutes parameter out of range".to_string()).into())?,
543                )
544                .ok_or::<RenderError>(
545                    RenderErrorReason::Other("Minutes parameter out of range or produces invalid date".to_string()).into(),
546                )?
547        } else {
548            datetime
549        };
550
551        let datetime = if let Some(sec) = h.hash_get("sub_seconds") {
552            let sec = sec.render().parse::<i64>().map_err(|e| {
553                <RenderErrorReason as Into<RenderError>>::into(RenderErrorReason::Other(format!("Invalid seconds parameter: {}", e)))
554            })?;
555
556            datetime
557                .checked_sub_signed(
558                    TimeDelta::try_seconds(sec)
559                        .ok_or::<RenderError>(RenderErrorReason::Other("Seconds parameter out of range".to_string()).into())?,
560                )
561                .ok_or::<RenderError>(
562                    RenderErrorReason::Other("Seconds parameter out of range or produces invalid date".to_string()).into(),
563                )?
564        } else {
565            datetime
566        };
567
568        let datetime = if let Some(msec) = h.hash_get("sub_milliseconds") {
569            let msec = msec.render().parse::<i64>().map_err(|e| {
570                <RenderErrorReason as Into<RenderError>>::into(RenderErrorReason::Other(format!("Invalid milli-seconds parameter: {}", e)))
571            })?;
572
573            datetime
574                .checked_sub_signed(
575                    TimeDelta::try_milliseconds(msec)
576                        .ok_or::<RenderError>(RenderErrorReason::Other("Milli-seconds parameter out of range".to_string()).into())?,
577                )
578                .ok_or::<RenderError>(
579                    RenderErrorReason::Other("Milli-seconds parameter out of range or produces invalid date".to_string()).into(),
580                )?
581        } else {
582            datetime
583        };
584
585        let datetime = if let Some(usec) = h.hash_get("sub_microseconds") {
586            let usec = usec.render().parse::<i64>().map_err(|e| {
587                <RenderErrorReason as Into<RenderError>>::into(RenderErrorReason::Other(format!("Invalid micro-seconds parameter: {}", e)))
588            })?;
589
590            datetime.checked_sub_signed(TimeDelta::microseconds(usec)).ok_or::<RenderError>(
591                RenderErrorReason::Other("Micro-seconds parameter out of range or produces invalid date".to_string()).into(),
592            )?
593        } else {
594            datetime
595        };
596
597        let datetime = if let Some(nsec) = h.hash_get("sub_nanoseconds") {
598            let nsec = nsec.render().parse::<i64>().map_err(|e| {
599                <RenderErrorReason as Into<RenderError>>::into(RenderErrorReason::Other(format!("Invalid nano-seconds parameter: {}", e)))
600            })?;
601
602            datetime.checked_sub_signed(TimeDelta::nanoseconds(nsec)).ok_or::<RenderError>(
603                RenderErrorReason::Other("Nano-seconds parameter out of range or produces invalid date".to_string()).into(),
604            )?
605        } else {
606            datetime
607        };
608
609        // FINALIZERS
610
611        // format - output_format
612        // format_localized - output_format + locale
613        // to_rfc3339 (default)
614        // to_rfc2822
615        // timestamp
616        // timestamp_millis
617        // timestamp_micros
618        // timestamp_nanos
619        // years_since + (parse_from_rfc3339)
620        let output = if let Some(output_format) = h.hash_get("output_format") {
621            let output_format = output_format.render();
622
623            if let Some(locale) = h.hash_get("locale") {
624                let locale = locale.render();
625                #[cfg(feature = "locale")]
626                {
627                    let locale = Locale::from_str(&locale).map_err(|_e| {
628                        <RenderErrorReason as Into<RenderError>>::into(RenderErrorReason::Other(format!(
629                            "Invalid locale provided: {}",
630                            &locale
631                        )))
632                    })?;
633
634                    format!("{}", datetime.format_localized(&output_format, locale))
635                }
636                #[cfg(not(feature = "locale"))]
637                return Err(RenderErrorReason::Other(format!(
638                    "You need to enable the `locale` feature of `handlebars-chrono` for the `locale`={} param to work.",
639                    locale
640                ))
641                .into());
642            } else {
643                format!("{}", datetime.format(&output_format))
644            }
645        } else if h.hash_get("to_rfc2822").is_some() {
646            datetime.to_rfc2822()
647        } else if h.hash_get("to_timestamp").is_some() {
648            datetime.timestamp().to_string()
649        } else if h.hash_get("to_timestamp_millis").is_some() {
650            datetime.timestamp_millis().to_string()
651        } else if h.hash_get("to_timestamp_micros").is_some() {
652            datetime.timestamp_micros().to_string()
653        } else if h.hash_get("to_timestamp_nanos").is_some() {
654            datetime
655                .timestamp_nanos_opt()
656                .ok_or::<RenderError>(
657                    RenderErrorReason::Other(
658                        "An i64 with nanosecond precision can span a range of ~584 years. This timestamp is out of range.".to_string(),
659                    )
660                    .into(),
661                )?
662                .to_string()
663        } else if let Some(input_rfc3339) = h.hash_get("years_since") {
664            let input_rfc3339 = input_rfc3339.render();
665
666            let base_datetime = DateTime::parse_from_rfc3339(&input_rfc3339)
667                .map_err(|e| {
668                    <RenderErrorReason as Into<RenderError>>::into(RenderErrorReason::Other(format!(
669                        "Invalid RFC3339 datetime format: {}",
670                        e
671                    )))
672                })?
673                .to_utc();
674
675            datetime
676                .years_since(base_datetime.into())
677                .ok_or::<RenderError>(RenderErrorReason::Other("Negative range, try swapping the parameters.".to_string()).into())?
678                .to_string()
679        } else {
680            // DEFAULT to_rfc3339
681
682            datetime.to_rfc3339()
683        };
684
685        out.write(&output)?;
686
687        Ok(())
688    }
689}
690
691#[cfg(test)]
692mod tests {
693    use super::*;
694    use chrono::{NaiveDate, NaiveDateTime};
695
696    #[test]
697    fn it_works() {
698        use handlebars::Handlebars;
699
700        let mut h = Handlebars::new();
701        h.register_helper("datetime", Box::new(HandlebarsChronoDateTime));
702
703        // default: Utc::now() -> to_rfc3339
704        assert_eq!(
705            h.render_template(r#"{{datetime}}"#, &String::new())
706                .map(|s| s.as_str()[..16].to_string())
707                .expect("Render error"),
708            Utc::now().to_rfc3339().as_str()[..16].to_string(),
709            "Failed to render RFC3339 default output"
710        );
711
712        // default to output_format: Utc::now() -> format
713        let comparison = Utc::now().format("%Y-%m-%d %H:%M:%S").to_string();
714        assert_eq!(
715            h.render_template(r#"{{datetime output_format="%Y-%m-%d %H:%M:%S"}}"#, &String::new())
716                .expect("Render error"),
717            comparison,
718            "Failed to render format %Y-%m-%d %H:%M:%S"
719        );
720
721        // default to output_format + locale: Utc::now() -> format_localized
722        #[cfg(feature = "locale")]
723        let comparison = Utc::now().format_localized("%A, %B %e", Locale::fr_FR).to_string();
724        #[cfg(feature = "locale")]
725        assert_eq!(
726            h.render_template(r#"{{datetime output_format="%A, %B %e" locale="fr_FR"}}"#, &String::new())
727                .expect("Render error"),
728            comparison,
729            "Failed to render localized format %A, %B %C"
730        );
731
732        // default to to_rfc2822: Utc::now() -> to_rfc2822
733        let comparison = Utc::now().to_rfc2822();
734        assert_eq!(
735            h.render_template(r#"{{datetime to_rfc2822=true}}"#, &String::new())
736                .expect("Render error"),
737            comparison,
738            "Failed to render RFC2822"
739        );
740
741        // default to to_timestamp: Utc::now() -> timestamp
742        let comparison = Utc::now().timestamp().to_string();
743        assert_eq!(
744            h.render_template(r#"{{datetime to_timestamp=true}}"#, &String::new())
745                .expect("Render error"),
746            comparison,
747            "Failed to render timestamp"
748        );
749
750        // default to to_timestamp_millis: Utc::now() -> timestamp_millis
751        let comparison = Utc::now().timestamp_millis().to_string().as_str()[..9].to_string();
752        assert_eq!(
753            h.render_template(r#"{{datetime to_timestamp_millis=true}}"#, &String::new())
754                .map(|v| v.as_str()[..9].to_string())
755                .expect("Render error"),
756            comparison,
757            "Failed to render timestamp in milli-seconds"
758        );
759
760        // default to to_timestamp_micros: Utc::now() -> timestamp_micros
761        let comparison = Utc::now().timestamp_micros().to_string().as_str()[..9].to_string();
762        assert_eq!(
763            h.render_template(r#"{{datetime to_timestamp_micros=true}}"#, &String::new())
764                .map(|v| v.as_str()[..9].to_string())
765                .expect("Render error"),
766            comparison,
767            "Failed to render timestamp in micro-seconds"
768        );
769
770        // default to to_timestamp_nanos: Utc::now() -> timestamp_nanos
771        let comparison = Utc::now().timestamp_nanos_opt().unwrap_or(0).to_string().as_str()[..9].to_string();
772        assert_eq!(
773            h.render_template(r#"{{datetime to_timestamp_nanos=true}}"#, &String::new())
774                .map(|v| v.as_str()[..9].to_string())
775                .expect("Render error"),
776            comparison,
777            "Failed to render timestamp in nano-seconds"
778        );
779
780        // default to years_since + (parse_from_rfc3339):
781        let comparison = Utc::now()
782            .years_since(
783                NaiveDate::from_ymd_opt(1985, 6, 16)
784                    .unwrap()
785                    .and_hms_opt(12, 00, 00)
786                    .unwrap()
787                    .and_utc(),
788            )
789            .unwrap_or(0)
790            .to_string();
791        assert_eq!(
792            h.render_template(r#"{{datetime years_since="1985-06-16T12:00:00Z"}}"#, &String::new())
793                .expect("Render error"),
794            comparison,
795            "Failed to render years since"
796        );
797
798        //
799
800        // from_timestamp to default: from_timestamp -> to_rfc3339
801        let comparison = DateTime::from_timestamp(618658211, 0).unwrap().to_rfc3339();
802        assert_eq!(
803            h.render_template(r#"{{datetime from_timestamp="618658211"}}"#, &String::new())
804                .expect("Render error"),
805            comparison,
806            "Failed to render RFC3339 from timestamp"
807        );
808
809        // from_timestamp to output_format: from_timestamp -> format
810        let comparison = DateTime::from_timestamp(618658211, 0)
811            .unwrap()
812            .format("%Y-%m-%d %H:%M:%S")
813            .to_string();
814        assert_eq!(
815            h.render_template(
816                r#"{{datetime from_timestamp="618658211" output_format="%Y-%m-%d %H:%M:%S"}}"#,
817                &String::new()
818            )
819            .expect("Render error"),
820            comparison,
821            "Failed to render format %Y-%m-%d %H:%M:%S from timestamp"
822        );
823
824        // from_timestamp to output_format + locale: from_timestamp -> format_localized
825        #[cfg(feature = "locale")]
826        let comparison = DateTime::from_timestamp(618658211, 0)
827            .unwrap()
828            .format_localized("%A, %B %C", Locale::fr_FR)
829            .to_string();
830        #[cfg(feature = "locale")]
831        assert_eq!(
832            h.render_template(
833                r#"{{datetime from_timestamp="618658211" output_format="%A, %B %C" locale="fr_FR"}}"#,
834                &String::new()
835            )
836            .expect("Render error"),
837            comparison,
838            "Failed to render localized format %A, %B %C from timestamp"
839        );
840
841        // from_timestamp to to_rfc2822: from_timestamp -> to_rfc2822
842        let comparison = DateTime::from_timestamp(618658211, 0).unwrap().to_rfc2822();
843        assert_eq!(
844            h.render_template(r#"{{datetime from_timestamp="618658211" to_rfc2822=true}}"#, &String::new())
845                .expect("Render error"),
846            comparison,
847            "Failed to render RFC2822 from timestamp"
848        );
849
850        // from_timestamp to to_timestamp: from_timestamp -> timestamp
851        let comparison = DateTime::from_timestamp(618658211, 0).unwrap().timestamp().to_string();
852        assert_eq!(
853            h.render_template(r#"{{datetime from_timestamp="618658211" to_timestamp=true}}"#, &String::new())
854                .expect("Render error"),
855            comparison,
856            "Failed to render timestamp from timestamp"
857        );
858
859        //
860
861        // default to to_timestamp_millis: from_timestamp -> timestamp_millis
862        let comparison = DateTime::from_timestamp(618658211, 0)
863            .unwrap()
864            .timestamp_millis()
865            .to_string()
866            .as_str()[..9]
867            .to_string();
868        assert_eq!(
869            h.render_template(
870                r#"{{datetime from_timestamp="618658211" to_timestamp_millis=true}}"#,
871                &String::new()
872            )
873            .map(|v| v.as_str()[..9].to_string())
874            .expect("Render error"),
875            comparison,
876            "Failed to render timestamp in milli-seconds from timestamp"
877        );
878
879        // from_timestamp to to_timestamp_micros: from_timestamp -> timestamp_micros
880        let comparison = DateTime::from_timestamp(618658211, 0)
881            .unwrap()
882            .timestamp_micros()
883            .to_string()
884            .as_str()[..9]
885            .to_string();
886        assert_eq!(
887            h.render_template(
888                r#"{{datetime from_timestamp="618658211" to_timestamp_micros=true}}"#,
889                &String::new()
890            )
891            .map(|v| v.as_str()[..9].to_string())
892            .expect("Render error"),
893            comparison,
894            "Failed to render timestamp in micro-seconds from timestamp"
895        );
896
897        // from_timestamp to to_timestamp_nanos: from_timestamp -> timestamp_nanos
898        let comparison = DateTime::from_timestamp(618658211, 0)
899            .unwrap()
900            .timestamp_nanos_opt()
901            .unwrap_or(0)
902            .to_string()
903            .as_str()[..9]
904            .to_string();
905        assert_eq!(
906            h.render_template(r#"{{datetime from_timestamp="618658211" to_timestamp_nanos=true}}"#, &String::new())
907                .map(|v| v.as_str()[..9].to_string())
908                .expect("Render error"),
909            comparison,
910            "Failed to render timestamp in nano-seconds from timestamp"
911        );
912
913        // from_timestamp to years_since + (parse_from_rfc3339)
914        let comparison = DateTime::from_timestamp(618658211, 0)
915            .unwrap()
916            .years_since(
917                NaiveDate::from_ymd_opt(1985, 6, 16)
918                    .unwrap()
919                    .and_hms_opt(12, 00, 00)
920                    .unwrap()
921                    .and_utc(),
922            )
923            .unwrap_or(0)
924            .to_string();
925        assert_eq!(
926            h.render_template(
927                r#"{{datetime from_timestamp="618658211" years_since="1985-06-16T12:00:00Z"}}"#,
928                &String::new()
929            )
930            .expect("Render error"),
931            comparison,
932            "Failed to render years since from timestamp"
933        );
934        assert_eq!(
935            h.render_template(
936                r#"{{datetime from_timestamp="618658211" years_since=(datetime from_timestamp="487771200")}}"#,
937                &String::new()
938            )
939            .expect("Render error"),
940            comparison,
941            "Failed to render years since from timestamp"
942        );
943
944        //
945
946        // from_timestamp_millis to default: from_timestamp_millis -> to_rfc3339
947        let comparison = DateTime::from_timestamp_millis(618658211123).unwrap().to_rfc3339();
948        assert_eq!(
949            h.render_template(r#"{{datetime from_timestamp_millis="618658211123"}}"#, &String::new())
950                .expect("Render error"),
951            comparison,
952            "Failed to render RFC3339 from timestamp in milli-seconds"
953        );
954
955        // from_timestamp_millis to output_format: from_timestamp_millis -> format
956        let comparison = DateTime::from_timestamp_millis(618658211123)
957            .unwrap()
958            .format("%Y-%m-%d %H:%M:%S")
959            .to_string();
960        assert_eq!(
961            h.render_template(
962                r#"{{datetime from_timestamp_millis="618658211123" output_format="%Y-%m-%d %H:%M:%S"}}"#,
963                &String::new()
964            )
965            .expect("Render error"),
966            comparison,
967            "Failed to render format %Y-%m-%d %H:%M:%S from timestamp in milli-seconds"
968        );
969
970        // from_timestamp_millis to output_format + locale: from_timestamp_millis -> format_localized
971        #[cfg(feature = "locale")]
972        let comparison = DateTime::from_timestamp_millis(618658211123)
973            .unwrap()
974            .format_localized("%A, %B %e", Locale::fr_FR)
975            .to_string();
976        #[cfg(feature = "locale")]
977        assert_eq!(
978            h.render_template(
979                r#"{{datetime from_timestamp_millis="618658211123" output_format="%A, %B %e" locale="fr_FR"}}"#,
980                &String::new()
981            )
982            .expect("Render error"),
983            comparison,
984            "Failed to render localized format %A, %B %C from timestamp in milli-seconds"
985        );
986
987        // from_timestamp_millis to to_rfc2822: from_timestamp_millis -> to_rfc2822
988        let comparison = DateTime::from_timestamp_millis(618658211123).unwrap().to_rfc2822();
989        assert_eq!(
990            h.render_template(
991                r#"{{datetime from_timestamp_millis="618658211123" to_rfc2822=true}}"#,
992                &String::new()
993            )
994            .expect("Render error"),
995            comparison,
996            "Failed to render RFC2822 from timestamp in milli-seconds"
997        );
998
999        // from_timestamp_millis to to_timestamp: from_timestamp_millis -> timestamp
1000        let comparison = DateTime::from_timestamp_millis(618658211123).unwrap().timestamp().to_string();
1001        assert_eq!(
1002            h.render_template(
1003                r#"{{datetime from_timestamp_millis="618658211123" to_timestamp=true}}"#,
1004                &String::new()
1005            )
1006            .expect("Render error"),
1007            comparison,
1008            "Failed to render timestamp from timestamp in milli-seconds"
1009        );
1010
1011        // from_timestamp_millis to to_timestamp_millis: from_timestamp_millis -> timestamp_millis
1012        let comparison = DateTime::from_timestamp_millis(618658211123)
1013            .unwrap()
1014            .timestamp_millis()
1015            .to_string()
1016            .as_str()[..9]
1017            .to_string();
1018        assert_eq!(
1019            h.render_template(
1020                r#"{{datetime from_timestamp_millis="618658211123" to_timestamp_millis=true}}"#,
1021                &String::new()
1022            )
1023            .map(|v| v.as_str()[..9].to_string())
1024            .expect("Render error"),
1025            comparison,
1026            "Failed to render timestamp in milli-seconds from timestamp in milli-seconds"
1027        );
1028
1029        // from_timestamp_millis to to_timestamp_micros: from_timestamp_millis -> timestamp_micros
1030        let comparison = DateTime::from_timestamp_millis(618658211123)
1031            .unwrap()
1032            .timestamp_micros()
1033            .to_string()
1034            .as_str()[..9]
1035            .to_string();
1036        assert_eq!(
1037            h.render_template(
1038                r#"{{datetime from_timestamp_millis="618658211123" to_timestamp_micros=true}}"#,
1039                &String::new()
1040            )
1041            .map(|v| v.as_str()[..9].to_string())
1042            .expect("Render error"),
1043            comparison,
1044            "Failed to render timestamp in micro-seconds from timestamp in milli-seconds"
1045        );
1046
1047        // from_timestamp_millis to to_timestamp_nanos: from_timestamp_millis -> timestamp_nanos
1048        let comparison = DateTime::from_timestamp_millis(618658211123)
1049            .unwrap()
1050            .timestamp_nanos_opt()
1051            .unwrap_or(0)
1052            .to_string()
1053            .as_str()[..9]
1054            .to_string();
1055        assert_eq!(
1056            h.render_template(
1057                r#"{{datetime from_timestamp_millis="618658211123" to_timestamp_nanos=true}}"#,
1058                &String::new()
1059            )
1060            .map(|v| v.as_str()[..9].to_string())
1061            .expect("Render error"),
1062            comparison,
1063            "Failed to render timestamp in nano-seconds from timestamp in milli-seconds"
1064        );
1065
1066        // from_timestamp_millis to years_since + (parse_from_rfc3339)
1067        let comparison = DateTime::from_timestamp_millis(618658211123)
1068            .unwrap()
1069            .years_since(
1070                NaiveDate::from_ymd_opt(1985, 6, 16)
1071                    .unwrap()
1072                    .and_hms_opt(12, 00, 00)
1073                    .unwrap()
1074                    .and_utc(),
1075            )
1076            .unwrap_or(0)
1077            .to_string();
1078        assert_eq!(
1079            h.render_template(
1080                r#"{{datetime from_timestamp_millis="618658211123" years_since="1985-06-16T12:00:00Z"}}"#,
1081                &String::new()
1082            )
1083            .expect("Render error"),
1084            comparison,
1085            "Failed to render years since from timestamp in milli-seconds"
1086        );
1087
1088        //
1089
1090        // from_timestamp_micros to default: from_timestamp_micros -> to_rfc3339
1091        let comparison = DateTime::from_timestamp_micros(618658211123456).unwrap().to_rfc3339();
1092        assert_eq!(
1093            h.render_template(r#"{{datetime from_timestamp_micros="618658211123456"}}"#, &String::new())
1094                .expect("Render error"),
1095            comparison,
1096            "Failed to render RFC3339 from timestamp in micro-seconds"
1097        );
1098
1099        // from_timestamp_micros to output_format: from_timestamp_micros -> format
1100        let comparison = DateTime::from_timestamp_micros(618658211123456)
1101            .unwrap()
1102            .format("%Y-%m-%d %H:%M:%S")
1103            .to_string();
1104        assert_eq!(
1105            h.render_template(
1106                r#"{{datetime from_timestamp_micros="618658211123456" output_format="%Y-%m-%d %H:%M:%S"}}"#,
1107                &String::new()
1108            )
1109            .expect("Render error"),
1110            comparison,
1111            "Failed to render format %Y-%m-%d %H:%M:%S from timestamp in micro-seconds"
1112        );
1113
1114        // from_timestamp_micros to output_format + locale: from_timestamp_micros -> format_localized
1115        #[cfg(feature = "locale")]
1116        let comparison = DateTime::from_timestamp_micros(618658211123456)
1117            .unwrap()
1118            .format_localized("%A, %B %C", Locale::fr_FR)
1119            .to_string();
1120        #[cfg(feature = "locale")]
1121        assert_eq!(
1122            h.render_template(
1123                r#"{{datetime from_timestamp_micros="618658211123456" output_format="%A, %B %C" locale="fr_FR"}}"#,
1124                &String::new()
1125            )
1126            .expect("Render error"),
1127            comparison,
1128            "Failed to render localized format %A, %B %C from timestamp in micro-seconds"
1129        );
1130
1131        // from_timestamp_micros to to_rfc2822: from_timestamp_micros -> to_rfc2822
1132        let comparison = DateTime::from_timestamp_micros(618658211123456).unwrap().to_rfc2822();
1133        assert_eq!(
1134            h.render_template(
1135                r#"{{datetime from_timestamp_micros="618658211123456" to_rfc2822=true}}"#,
1136                &String::new()
1137            )
1138            .expect("Render error"),
1139            comparison,
1140            "Failed to render RFC2822 from timestamp in micro-seconds"
1141        );
1142
1143        // from_timestamp_micros to to_timestamp: from_timestamp_micros -> timestamp
1144        let comparison = DateTime::from_timestamp_micros(618658211123456).unwrap().timestamp().to_string();
1145        assert_eq!(
1146            h.render_template(
1147                r#"{{datetime from_timestamp_micros="618658211123456" to_timestamp=true}}"#,
1148                &String::new()
1149            )
1150            .expect("Render error"),
1151            comparison,
1152            "Failed to render timestamp from timestamp in micro-seconds"
1153        );
1154
1155        // from_timestamp_micros to to_timestamp_millis: from_timestamp_micros -> timestamp_millis
1156        let comparison = DateTime::from_timestamp_micros(618658211123456)
1157            .unwrap()
1158            .timestamp_millis()
1159            .to_string()
1160            .as_str()[..9]
1161            .to_string();
1162        assert_eq!(
1163            h.render_template(
1164                r#"{{datetime from_timestamp_micros="618658211123456" to_timestamp_millis=true}}"#,
1165                &String::new()
1166            )
1167            .map(|v| v.as_str()[..9].to_string())
1168            .expect("Render error"),
1169            comparison,
1170            "Failed to render timestamp in milli-seconds from timestamp in micro-seconds"
1171        );
1172
1173        // from_timestamp_micros to to_timestamp_micros: from_timestamp_micros -> timestamp_micros
1174        let comparison = DateTime::from_timestamp_micros(618658211123456)
1175            .unwrap()
1176            .timestamp_micros()
1177            .to_string()
1178            .as_str()[..9]
1179            .to_string();
1180        assert_eq!(
1181            h.render_template(
1182                r#"{{datetime from_timestamp_micros="618658211123456" to_timestamp_micros=true}}"#,
1183                &String::new()
1184            )
1185            .map(|v| v.as_str()[..9].to_string())
1186            .expect("Render error"),
1187            comparison,
1188            "Failed to render timestamp in micro-seconds from timestamp in micro-seconds"
1189        );
1190
1191        // from_timestamp_micros to to_timestamp_nanos: from_timestamp_micros -> timestamp_nanos
1192        let comparison = DateTime::from_timestamp_micros(618658211123456)
1193            .unwrap()
1194            .timestamp_nanos_opt()
1195            .unwrap_or(0)
1196            .to_string()
1197            .as_str()[..9]
1198            .to_string();
1199        assert_eq!(
1200            h.render_template(
1201                r#"{{datetime from_timestamp_micros="618658211123456" to_timestamp_nanos=true}}"#,
1202                &String::new()
1203            )
1204            .map(|v| v.as_str()[..9].to_string())
1205            .expect("Render error"),
1206            comparison,
1207            "Failed to render timestamp in nano-seconds from timestamp in micro-seconds"
1208        );
1209
1210        // from_timestamp_micros to years_since + (parse_from_rfc3339)
1211        let comparison = DateTime::from_timestamp_micros(618658211123456)
1212            .unwrap()
1213            .years_since(
1214                NaiveDate::from_ymd_opt(1985, 6, 16)
1215                    .unwrap()
1216                    .and_hms_opt(12, 00, 00)
1217                    .unwrap()
1218                    .and_utc(),
1219            )
1220            .unwrap_or(0)
1221            .to_string();
1222        assert_eq!(
1223            h.render_template(
1224                r#"{{datetime from_timestamp_micros="618658211123456" years_since="1985-06-16T12:00:00Z"}}"#,
1225                &String::new()
1226            )
1227            .expect("Render error"),
1228            comparison,
1229            "Failed to render years since from timestamp in micro-seconds"
1230        );
1231
1232        //
1233
1234        // from_timestamp_nanos to default: from_timestamp_nanos -> to_rfc3339
1235        let comparison = DateTime::from_timestamp_nanos(618658211123456789).to_rfc3339();
1236        assert_eq!(
1237            h.render_template(r#"{{datetime from_timestamp_nanos="618658211123456789"}}"#, &String::new())
1238                .expect("Render error"),
1239            comparison,
1240            "Failed to render RFC3339 from timestamp in nano-seconds"
1241        );
1242
1243        // from_timestamp_nanos to output_format: from_timestamp_nanos -> format
1244        let comparison = DateTime::from_timestamp_nanos(618658211123456789)
1245            .format("%Y-%m-%d %H:%M:%S")
1246            .to_string();
1247        assert_eq!(
1248            h.render_template(
1249                r#"{{datetime from_timestamp_nanos="618658211123456789" output_format="%Y-%m-%d %H:%M:%S"}}"#,
1250                &String::new()
1251            )
1252            .expect("Render error"),
1253            comparison,
1254            "Failed to render format %Y-%m-%d %H:%M:%S from timestamp in nano-seconds"
1255        );
1256
1257        // from_timestamp_nanos to output_format + locale: from_timestamp_nanos -> format_localized
1258        #[cfg(feature = "locale")]
1259        let comparison = DateTime::from_timestamp_nanos(618658211123456789)
1260            .format_localized("%A, %B %C", Locale::fr_FR)
1261            .to_string();
1262        #[cfg(feature = "locale")]
1263        assert_eq!(
1264            h.render_template(
1265                r#"{{datetime from_timestamp_nanos="618658211123456789" output_format="%A, %B %C" locale="fr_FR"}}"#,
1266                &String::new()
1267            )
1268            .expect("Render error"),
1269            comparison,
1270            "Failed to render localized format %A, %B %C from timestamp in nano-seconds"
1271        );
1272
1273        // from_timestamp_nanos to to_rfc2822: from_timestamp_nanos -> to_rfc2822
1274        let comparison = DateTime::from_timestamp_nanos(618658211123456789).to_rfc2822();
1275        assert_eq!(
1276            h.render_template(
1277                r#"{{datetime from_timestamp_nanos="618658211123456789" to_rfc2822=true}}"#,
1278                &String::new()
1279            )
1280            .expect("Render error"),
1281            comparison,
1282            "Failed to render RFC2822 from timestamp in nano-seconds"
1283        );
1284
1285        // from_timestamp_nanos to to_timestamp: from_timestamp_nanos -> timestamp
1286        let comparison = DateTime::from_timestamp_nanos(618658211123456789).timestamp().to_string();
1287        assert_eq!(
1288            h.render_template(
1289                r#"{{datetime from_timestamp_nanos="618658211123456789" to_timestamp=true}}"#,
1290                &String::new()
1291            )
1292            .expect("Render error"),
1293            comparison,
1294            "Failed to render timestamp from timestamp in nano-seconds"
1295        );
1296
1297        // from_timestamp_nanos to to_timestamp_millis: from_timestamp_nanos -> timestamp_millis
1298        let comparison = DateTime::from_timestamp_nanos(618658211123456789)
1299            .timestamp_millis()
1300            .to_string()
1301            .as_str()[..9]
1302            .to_string();
1303        assert_eq!(
1304            h.render_template(
1305                r#"{{datetime from_timestamp_nanos="618658211123456789" to_timestamp_millis=true}}"#,
1306                &String::new()
1307            )
1308            .map(|v| v.as_str()[..9].to_string())
1309            .expect("Render error"),
1310            comparison,
1311            "Failed to render timestamp in milli-seconds from timestamp in nano-seconds"
1312        );
1313
1314        // from_timestamp_nanos to to_timestamp_micros: from_timestamp_nanos -> timestamp_micros
1315        let comparison = DateTime::from_timestamp_nanos(618658211123456789)
1316            .timestamp_micros()
1317            .to_string()
1318            .as_str()[..9]
1319            .to_string();
1320        assert_eq!(
1321            h.render_template(
1322                r#"{{datetime from_timestamp_nanos="618658211123456789" to_timestamp_micros=true}}"#,
1323                &String::new()
1324            )
1325            .map(|v| v.as_str()[..9].to_string())
1326            .expect("Render error"),
1327            comparison,
1328            "Failed to render timestamp in micro-seconds from timestamp in nano-seconds"
1329        );
1330
1331        // from_timestamp_nanos to to_timestamp_nanos: from_timestamp_nanos -> timestamp_nanos
1332        let comparison = DateTime::from_timestamp_nanos(618658211123456789)
1333            .timestamp_nanos_opt()
1334            .unwrap_or(0)
1335            .to_string()
1336            .as_str()[..9]
1337            .to_string();
1338        assert_eq!(
1339            h.render_template(
1340                r#"{{datetime from_timestamp_nanos="618658211123456789" to_timestamp_nanos=true}}"#,
1341                &String::new()
1342            )
1343            .map(|v| v.as_str()[..9].to_string())
1344            .expect("Render error"),
1345            comparison,
1346            "Failed to render timestamp in nano-seconds from timestamp in nano-seconds"
1347        );
1348
1349        // from_timestamp_nanos to years_since + (parse_from_rfc3339)
1350        let comparison = DateTime::from_timestamp_nanos(618658211123456789)
1351            .years_since(
1352                NaiveDate::from_ymd_opt(1985, 6, 16)
1353                    .unwrap()
1354                    .and_hms_opt(12, 00, 00)
1355                    .unwrap()
1356                    .and_utc(),
1357            )
1358            .unwrap_or(0)
1359            .to_string();
1360        assert_eq!(
1361            h.render_template(
1362                r#"{{datetime from_timestamp_nanos="618658211123456789" years_since="1985-06-16T12:00:00Z"}}"#,
1363                &String::new()
1364            )
1365            .expect("Render error"),
1366            comparison,
1367            "Failed to render years since from timestamp in nano-seconds"
1368        );
1369
1370        //
1371
1372        // from_rfc2822 to default: parse_from_rfc2822 -> to_rfc3339
1373        let comparison = DateTime::parse_from_rfc2822("Wed, 09 Aug 1989 09:30:11 +0200")
1374            .unwrap()
1375            .to_utc()
1376            .to_rfc3339();
1377        assert_eq!(
1378            h.render_template(r#"{{datetime from_rfc2822="Wed, 09 Aug 1989 09:30:11 +0200"}}"#, &String::new())
1379                .expect("Render error"),
1380            comparison,
1381            "Failed to render RFC3339 from RFC2822"
1382        );
1383
1384        // from_rfc2822 to output_format: parse_from_rfc2822 -> format
1385        let comparison = DateTime::parse_from_rfc2822("Wed, 09 Aug 1989 09:30:11 +0200")
1386            .unwrap()
1387            .to_utc()
1388            .format("%Y-%m-%d %H:%M:%S")
1389            .to_string();
1390        assert_eq!(
1391            h.render_template(
1392                r#"{{datetime from_rfc2822="Wed, 09 Aug 1989 09:30:11 +0200" output_format="%Y-%m-%d %H:%M:%S"}}"#,
1393                &String::new()
1394            )
1395            .expect("Render error"),
1396            comparison,
1397            "Failed to render format %Y-%m-%d %H:%M:%S from RFC2822"
1398        );
1399
1400        // from_rfc2822 to output_format + locale: parse_from_rfc2822 -> format_localized
1401        #[cfg(feature = "locale")]
1402        let comparison = DateTime::parse_from_rfc2822("Wed, 09 Aug 1989 09:30:11 +0200")
1403            .unwrap()
1404            .to_utc()
1405            .format_localized("%A, %B %C", Locale::fr_FR)
1406            .to_string();
1407        #[cfg(feature = "locale")]
1408        assert_eq!(
1409            h.render_template(
1410                r#"{{datetime from_rfc2822="Wed, 09 Aug 1989 09:30:11 +0200" output_format="%A, %B %C" locale="fr_FR"}}"#,
1411                &String::new()
1412            )
1413            .expect("Render error"),
1414            comparison,
1415            "Failed to render localized format %A, %B %C from RFC2822"
1416        );
1417
1418        // from_rfc2822 to to_rfc2822: parse_from_rfc2822 -> to_rfc2822
1419        let comparison = DateTime::parse_from_rfc2822("Wed, 09 Aug 1989 09:30:11 +0200")
1420            .unwrap()
1421            .to_utc()
1422            .to_rfc2822();
1423        assert_eq!(
1424            h.render_template(
1425                r#"{{datetime from_rfc2822="Wed, 09 Aug 1989 09:30:11 +0200" to_rfc2822=true}}"#,
1426                &String::new()
1427            )
1428            .expect("Render error"),
1429            comparison,
1430            "Failed to render RFC2822 from RFC2822"
1431        );
1432
1433        // from_rfc2822 to to_timestamp: parse_from_rfc2822 -> timestamp
1434        let comparison = DateTime::parse_from_rfc2822("Wed, 09 Aug 1989 09:30:11 +0200")
1435            .unwrap()
1436            .to_utc()
1437            .timestamp()
1438            .to_string();
1439        assert_eq!(
1440            h.render_template(
1441                r#"{{datetime from_rfc2822="Wed, 09 Aug 1989 09:30:11 +0200" to_timestamp=true}}"#,
1442                &String::new()
1443            )
1444            .expect("Render error"),
1445            comparison,
1446            "Failed to render timestamp from RFC2822"
1447        );
1448
1449        // from_rfc2822 to to_timestamp_millis: parse_from_rfc2822 -> timestamp_millis
1450        let comparison = DateTime::parse_from_rfc2822("Wed, 09 Aug 1989 09:30:11 +0200")
1451            .unwrap()
1452            .to_utc()
1453            .timestamp_millis()
1454            .to_string()
1455            .as_str()[..9]
1456            .to_string();
1457        assert_eq!(
1458            h.render_template(
1459                r#"{{datetime from_rfc2822="Wed, 09 Aug 1989 09:30:11 +0200" to_timestamp_millis=true}}"#,
1460                &String::new()
1461            )
1462            .map(|v| v.as_str()[..9].to_string())
1463            .expect("Render error"),
1464            comparison,
1465            "Failed to render timestamp in milli-seconds from RFC2822"
1466        );
1467
1468        // from_rfc2822 to to_timestamp_micros: parse_from_rfc2822 -> timestamp_micros
1469        let comparison = DateTime::parse_from_rfc2822("Wed, 09 Aug 1989 09:30:11 +0200")
1470            .unwrap()
1471            .to_utc()
1472            .timestamp_micros()
1473            .to_string()
1474            .as_str()[..9]
1475            .to_string();
1476        assert_eq!(
1477            h.render_template(
1478                r#"{{datetime from_rfc2822="Wed, 09 Aug 1989 09:30:11 +0200" to_timestamp_micros=true}}"#,
1479                &String::new()
1480            )
1481            .map(|v| v.as_str()[..9].to_string())
1482            .expect("Render error"),
1483            comparison,
1484            "Failed to render timestamp in micro-seconds from RFC2822"
1485        );
1486
1487        // from_rfc2822 to to_timestamp_nanos: parse_from_rfc2822 -> timestamp_nanos
1488        let comparison = DateTime::parse_from_rfc2822("Wed, 09 Aug 1989 09:30:11 +0200")
1489            .unwrap()
1490            .to_utc()
1491            .timestamp_nanos_opt()
1492            .unwrap_or(0)
1493            .to_string()
1494            .as_str()[..9]
1495            .to_string();
1496        assert_eq!(
1497            h.render_template(
1498                r#"{{datetime from_rfc2822="Wed, 09 Aug 1989 09:30:11 +0200" to_timestamp_nanos=true}}"#,
1499                &String::new()
1500            )
1501            .map(|v| v.as_str()[..9].to_string())
1502            .expect("Render error"),
1503            comparison,
1504            "Failed to render timestamp in nano-seconds from RFC2822"
1505        );
1506
1507        // from_rfc2822 to years_since + (parse_from_rfc3339)
1508        let comparison = DateTime::parse_from_rfc2822("Wed, 09 Aug 1989 09:30:11 +0200")
1509            .unwrap()
1510            .to_utc()
1511            .years_since(
1512                NaiveDate::from_ymd_opt(1985, 6, 16)
1513                    .unwrap()
1514                    .and_hms_opt(12, 00, 00)
1515                    .unwrap()
1516                    .and_utc(),
1517            )
1518            .unwrap_or(0)
1519            .to_string();
1520        assert_eq!(
1521            h.render_template(
1522                r#"{{datetime from_rfc2822="Wed, 09 Aug 1989 09:30:11 +0200" years_since="1985-06-16T12:00:00Z"}}"#,
1523                &String::new()
1524            )
1525            .expect("Render error"),
1526            comparison,
1527            "Failed to render years since from RFC2822"
1528        );
1529
1530        //
1531
1532        // from_rfc3339 to default: parse_from_rfc3339 -> to_rfc3339
1533        let comparison = DateTime::parse_from_rfc3339("1989-08-09T09:30:11+02:00")
1534            .unwrap()
1535            .to_utc()
1536            .to_rfc3339();
1537        assert_eq!(
1538            h.render_template(r#"{{datetime from_rfc3339="1989-08-09T09:30:11+02:00"}}"#, &String::new())
1539                .expect("Render error"),
1540            comparison,
1541            "Failed to render RFC3339 from RFC3339"
1542        );
1543
1544        // from_rfc3339 to output_format: parse_from_rfc3339 -> format
1545        let comparison = DateTime::parse_from_rfc3339("1989-08-09T09:30:11+02:00")
1546            .unwrap()
1547            .to_utc()
1548            .format("%Y-%m-%d %H:%M:%S")
1549            .to_string();
1550        assert_eq!(
1551            h.render_template(
1552                r#"{{datetime from_rfc3339="1989-08-09T09:30:11+02:00" output_format="%Y-%m-%d %H:%M:%S"}}"#,
1553                &String::new()
1554            )
1555            .expect("Render error"),
1556            comparison,
1557            "Failed to render format %Y-%m-%d %H:%M:%S from RFC3339"
1558        );
1559
1560        // from_rfc3339 to output_format + locale: parse_from_rfc3339 -> format_localized
1561        #[cfg(feature = "locale")]
1562        let comparison = DateTime::parse_from_rfc3339("1989-08-09T09:30:11+02:00")
1563            .unwrap()
1564            .to_utc()
1565            .format_localized("%A, %B %C", Locale::fr_FR)
1566            .to_string();
1567        #[cfg(feature = "locale")]
1568        assert_eq!(
1569            h.render_template(
1570                r#"{{datetime from_rfc3339="1989-08-09T09:30:11+02:00" output_format="%A, %B %C" locale="fr_FR"}}"#,
1571                &String::new()
1572            )
1573            .expect("Render error"),
1574            comparison,
1575            "Failed to render localized format %A, %B %C from RFC3339"
1576        );
1577
1578        // from_rfc3339 to to_rfc2822: parse_from_rfc3339 -> to_rfc2822
1579        let comparison = DateTime::parse_from_rfc3339("1989-08-09T09:30:11+02:00")
1580            .unwrap()
1581            .to_utc()
1582            .to_rfc2822();
1583        assert_eq!(
1584            h.render_template(
1585                r#"{{datetime from_rfc3339="1989-08-09T09:30:11+02:00" to_rfc2822=true}}"#,
1586                &String::new()
1587            )
1588            .expect("Render error"),
1589            comparison,
1590            "Failed to render RFC2822 from RFC3339"
1591        );
1592
1593        // from_rfc3339 to to_timestamp: parse_from_rfc3339 -> timestamp
1594        let comparison = DateTime::parse_from_rfc3339("1989-08-09T09:30:11+02:00")
1595            .unwrap()
1596            .to_utc()
1597            .timestamp()
1598            .to_string();
1599        assert_eq!(
1600            h.render_template(
1601                r#"{{datetime from_rfc3339="1989-08-09T09:30:11+02:00" to_timestamp=true}}"#,
1602                &String::new()
1603            )
1604            .expect("Render error"),
1605            comparison,
1606            "Failed to render timestamp from RFC3339"
1607        );
1608
1609        // from_rfc3339 to to_timestamp_millis: parse_from_rfc3339 -> timestamp_millis
1610        let comparison = DateTime::parse_from_rfc3339("1989-08-09T09:30:11+02:00")
1611            .unwrap()
1612            .to_utc()
1613            .timestamp_millis()
1614            .to_string()
1615            .as_str()[..9]
1616            .to_string();
1617        assert_eq!(
1618            h.render_template(
1619                r#"{{datetime from_rfc3339="1989-08-09T09:30:11+02:00" to_timestamp_millis=true}}"#,
1620                &String::new()
1621            )
1622            .map(|v| v.as_str()[..9].to_string())
1623            .expect("Render error"),
1624            comparison,
1625            "Failed to render timestamp in milli-seconds from RFC3339"
1626        );
1627
1628        // from_rfc3339 to to_timestamp_micros: parse_from_rfc3339 -> timestamp_micros
1629        let comparison = DateTime::parse_from_rfc3339("1989-08-09T09:30:11+02:00")
1630            .unwrap()
1631            .to_utc()
1632            .timestamp_micros()
1633            .to_string()
1634            .as_str()[..9]
1635            .to_string();
1636        assert_eq!(
1637            h.render_template(
1638                r#"{{datetime from_rfc3339="1989-08-09T09:30:11+02:00" to_timestamp_micros=true}}"#,
1639                &String::new()
1640            )
1641            .map(|v| v.as_str()[..9].to_string())
1642            .expect("Render error"),
1643            comparison,
1644            "Failed to render timestamp in micro-seconds from RFC3339"
1645        );
1646
1647        // from_rfc3339 to to_timestamp_nanos: parse_from_rfc3339 -> timestamp_nanos
1648        let comparison = DateTime::parse_from_rfc3339("1989-08-09T09:30:11+02:00")
1649            .unwrap()
1650            .to_utc()
1651            .timestamp_nanos_opt()
1652            .unwrap_or(0)
1653            .to_string()
1654            .as_str()[..9]
1655            .to_string();
1656        assert_eq!(
1657            h.render_template(
1658                r#"{{datetime from_rfc3339="1989-08-09T09:30:11+02:00" to_timestamp_nanos=true}}"#,
1659                &String::new()
1660            )
1661            .map(|v| v.as_str()[..9].to_string())
1662            .expect("Render error"),
1663            comparison,
1664            "Failed to render timestamp in nano-seconds from RFC3339"
1665        );
1666
1667        // from_rfc3339 to years_since + (parse_from_rfc3339)
1668        let comparison = DateTime::parse_from_rfc3339("1989-08-09T09:30:11+02:00")
1669            .unwrap()
1670            .to_utc()
1671            .years_since(
1672                NaiveDate::from_ymd_opt(1985, 6, 16)
1673                    .unwrap()
1674                    .and_hms_opt(12, 00, 00)
1675                    .unwrap()
1676                    .and_utc(),
1677            )
1678            .unwrap_or(0)
1679            .to_string();
1680        assert_eq!(
1681            h.render_template(
1682                r#"{{datetime from_rfc3339="1989-08-09T09:30:11+02:00" years_since="1985-06-16T12:00:00Z"}}"#,
1683                &String::new()
1684            )
1685            .expect("Render error"),
1686            comparison,
1687            "Failed to render years since from RFC3339"
1688        );
1689
1690        //
1691
1692        //  + input_format
1693
1694        // from_str to default: parse_from_str -> to_rfc3339
1695        let comparison = NaiveDateTime::parse_from_str("1989-08-09 09:30:11", "%Y-%m-%d %H:%M:%S")
1696            .unwrap()
1697            .and_utc()
1698            .to_rfc3339();
1699        assert_eq!(
1700            h.render_template(
1701                r#"{{datetime from_str="1989-08-09 09:30:11" input_format="%Y-%m-%d %H:%M:%S"}}"#,
1702                &String::new()
1703            )
1704            .expect("Render error"),
1705            comparison,
1706            "Failed to render RFC3339 from %Y-%m-%d %H:%M:%S string"
1707        );
1708
1709        // from_str to output_format: parse_from_rfc3339 -> format
1710        let comparison = NaiveDateTime::parse_from_str("1989-08-09 09:30:11", "%Y-%m-%d %H:%M:%S")
1711            .unwrap()
1712            .and_utc()
1713            .format("%Y-%d-%m %H:%M")
1714            .to_string();
1715        assert_eq!(
1716            h.render_template(
1717                r#"{{datetime from_str="1989-08-09 09:30:11" input_format="%Y-%m-%d %H:%M:%S" output_format="%Y-%d-%m %H:%M"}}"#,
1718                &String::new()
1719            )
1720            .expect("Render error"),
1721            comparison,
1722            "Failed to render format %Y-%d-%m %H:%M from %Y-%m-%d %H:%M:%S string"
1723        );
1724
1725        // from_str to output_format + locale: parse_from_str -> format_localized
1726        #[cfg(feature = "locale")]
1727        let comparison = NaiveDateTime::parse_from_str("1989-08-09 09:30:11", "%Y-%m-%d %H:%M:%S")
1728            .unwrap()
1729            .and_utc()
1730            .format_localized("%A, %B %C", Locale::fr_FR)
1731            .to_string();
1732        #[cfg(feature = "locale")]
1733        assert_eq!(
1734            h.render_template(
1735                r#"{{datetime from_str="1989-08-09 09:30:11" input_format="%Y-%m-%d %H:%M:%S" output_format="%A, %B %C" locale="fr_FR"}}"#,
1736                &String::new()
1737            )
1738            .expect("Render error"),
1739            comparison,
1740            "Failed to render localized format %A, %B %C from %Y-%m-%d %H:%M:%S string"
1741        );
1742
1743        // from_str to to_rfc2822: parse_from_str -> to_rfc2822
1744        let comparison = NaiveDateTime::parse_from_str("1989-08-09 09:30:11", "%Y-%m-%d %H:%M:%S")
1745            .unwrap()
1746            .and_utc()
1747            .to_rfc2822();
1748        assert_eq!(
1749            h.render_template(
1750                r#"{{datetime from_str="1989-08-09 09:30:11" input_format="%Y-%m-%d %H:%M:%S" to_rfc2822=true}}"#,
1751                &String::new()
1752            )
1753            .expect("Render error"),
1754            comparison,
1755            "Failed to render RFC2822 from %Y-%m-%d %H:%M:%S string"
1756        );
1757
1758        // from_str to to_timestamp: parse_from_str -> timestamp
1759        let comparison = NaiveDateTime::parse_from_str("1989-08-09 09:30:11", "%Y-%m-%d %H:%M:%S")
1760            .unwrap()
1761            .and_utc()
1762            .timestamp()
1763            .to_string();
1764        assert_eq!(
1765            h.render_template(
1766                r#"{{datetime from_str="1989-08-09 09:30:11" input_format="%Y-%m-%d %H:%M:%S" to_timestamp=true}}"#,
1767                &String::new()
1768            )
1769            .expect("Render error"),
1770            comparison,
1771            "Failed to render timestamp from %Y-%m-%d %H:%M:%S string"
1772        );
1773
1774        // from_str to to_timestamp_millis: parse_from_str -> timestamp_millis
1775        let comparison = NaiveDateTime::parse_from_str("1989-08-09 09:30:11", "%Y-%m-%d %H:%M:%S")
1776            .unwrap()
1777            .and_utc()
1778            .timestamp_millis()
1779            .to_string()
1780            .as_str()[..9]
1781            .to_string();
1782        assert_eq!(
1783            h.render_template(
1784                r#"{{datetime from_str="1989-08-09 09:30:11" input_format="%Y-%m-%d %H:%M:%S" to_timestamp_millis=true}}"#,
1785                &String::new()
1786            )
1787            .map(|v| v.as_str()[..9].to_string())
1788            .expect("Render error"),
1789            comparison,
1790            "Failed to render timestamp in milli-seconds from %Y-%m-%d %H:%M:%S string"
1791        );
1792
1793        // from_str to to_timestamp_micros: parse_from_str -> timestamp_micros
1794        let comparison = NaiveDateTime::parse_from_str("1989-08-09 09:30:11", "%Y-%m-%d %H:%M:%S")
1795            .unwrap()
1796            .and_utc()
1797            .timestamp_micros()
1798            .to_string()
1799            .as_str()[..9]
1800            .to_string();
1801        assert_eq!(
1802            h.render_template(
1803                r#"{{datetime from_str="1989-08-09 09:30:11" input_format="%Y-%m-%d %H:%M:%S" to_timestamp_micros=true}}"#,
1804                &String::new()
1805            )
1806            .map(|v| v.as_str()[..9].to_string())
1807            .expect("Render error"),
1808            comparison,
1809            "Failed to render timestamp in micro-seconds from %Y-%m-%d %H:%M:%S string"
1810        );
1811
1812        // from_str to to_timestamp_nanos: parse_from_str -> timestamp_nanos
1813        let comparison = NaiveDateTime::parse_from_str("1989-08-09 09:30:11", "%Y-%m-%d %H:%M:%S")
1814            .unwrap()
1815            .and_utc()
1816            .timestamp_nanos_opt()
1817            .unwrap_or(0)
1818            .to_string()
1819            .as_str()[..9]
1820            .to_string();
1821        assert_eq!(
1822            h.render_template(
1823                r#"{{datetime from_str="1989-08-09 09:30:11" input_format="%Y-%m-%d %H:%M:%S" to_timestamp_nanos=true}}"#,
1824                &String::new()
1825            )
1826            .map(|v| v.as_str()[..9].to_string())
1827            .expect("Render error"),
1828            comparison,
1829            "Failed to render timestamp in nano-seconds from %Y-%m-%d %H:%M:%S string"
1830        );
1831
1832        // from_str to years_since + (parse_from_rfc3339)
1833        let comparison = NaiveDateTime::parse_from_str("1989-08-09 09:30:11", "%Y-%m-%d %H:%M:%S")
1834            .unwrap()
1835            .and_utc()
1836            .years_since(
1837                NaiveDate::from_ymd_opt(1985, 6, 16)
1838                    .unwrap()
1839                    .and_hms_opt(12, 00, 00)
1840                    .unwrap()
1841                    .and_utc(),
1842            )
1843            .unwrap_or(0)
1844            .to_string();
1845        assert_eq!(
1846            h.render_template(
1847                r#"{{datetime from_str="1989-08-09 09:30:11" input_format="%Y-%m-%d %H:%M:%S" years_since="1985-06-16T12:00:00Z"}}"#,
1848                &String::new()
1849            )
1850            .expect("Render error"),
1851            comparison,
1852            "Failed to render years since from %Y-%m-%d %H:%M:%S string"
1853        );
1854
1855        // modifiers
1856
1857        #[cfg(feature = "timezone")]
1858        let comparison = DateTime::parse_from_rfc3339("1989-08-09T09:30:11+02:00")
1859            .unwrap()
1860            .to_utc()
1861            .with_timezone(&Tz::America__Edmonton)
1862            .to_rfc3339();
1863        #[cfg(feature = "timezone")]
1864        assert_eq!(
1865            h.render_template(
1866                r#"{{datetime from_rfc3339="1989-08-09T09:30:11+02:00" with_timezone="America/Edmonton"}}"#,
1867                &String::new()
1868            )
1869            .expect("Render error"),
1870            comparison,
1871            "Failed to render RFC3339 from RFC3339 with timezone America/Edmonton"
1872        );
1873
1874        let comparison = DateTime::parse_from_rfc3339("1989-08-09T09:30:11+02:00")
1875            .unwrap()
1876            .to_utc()
1877            .with_timezone(&FixedOffset::west_opt(6 * 3600).unwrap())
1878            .to_rfc3339();
1879        assert_eq!(
1880            h.render_template(
1881                r#"{{datetime from_rfc3339="1989-08-09T09:30:11+02:00" with_timezone="-06:00"}}"#,
1882                &String::new()
1883            )
1884            .expect("Render error"),
1885            comparison,
1886            "Failed to render RFC3339 from RFC3339 with fixed offset -06:00"
1887        );
1888
1889        let comparison = DateTime::parse_from_rfc3339("1989-08-09T09:30:11+02:00")
1890            .unwrap()
1891            .to_utc()
1892            .with_timezone(&Local)
1893            .to_rfc3339();
1894        assert_eq!(
1895            h.render_template(
1896                r#"{{datetime from_rfc3339="1989-08-09T09:30:11+02:00" with_timezone="local"}}"#,
1897                &String::new()
1898            )
1899            .expect("Render error"),
1900            comparison,
1901            "Failed to render RFC3339 from RFC3339 in local time"
1902        );
1903
1904        let comparison = DateTime::from_timestamp(618658211, 0)
1905            .unwrap()
1906            .with_ordinal(42)
1907            .unwrap()
1908            .timestamp()
1909            .to_string();
1910        assert_eq!(
1911            h.render_template(
1912                r#"{{datetime from_timestamp="618658211" with_ordinal="42" to_timestamp=true}}"#,
1913                &String::new()
1914            )
1915            .expect("Render error"),
1916            comparison,
1917            "Failed to render timestamp from timestamp with ordinal 42"
1918        );
1919
1920        let comparison = DateTime::from_timestamp(618658211, 0)
1921            .unwrap()
1922            .with_ordinal0(41)
1923            .unwrap()
1924            .timestamp()
1925            .to_string();
1926        assert_eq!(
1927            h.render_template(
1928                r#"{{datetime from_timestamp="618658211" with_ordinal0="41" to_timestamp=true}}"#,
1929                &String::new()
1930            )
1931            .expect("Render error"),
1932            comparison,
1933            "Failed to render timestamp from timestamp with ordinal0 41"
1934        );
1935
1936        let comparison = DateTime::from_timestamp(618658211, 0)
1937            .unwrap()
1938            .with_year(2024)
1939            .unwrap()
1940            .timestamp()
1941            .to_string();
1942        assert_eq!(
1943            h.render_template(
1944                r#"{{datetime from_timestamp="618658211" with_year="2024" to_timestamp=true}}"#,
1945                &String::new()
1946            )
1947            .expect("Render error"),
1948            comparison,
1949            "Failed to render timestamp from timestamp with year 2024"
1950        );
1951
1952        let comparison = DateTime::from_timestamp(618658211, 0)
1953            .unwrap()
1954            .with_month(11)
1955            .unwrap()
1956            .timestamp()
1957            .to_string();
1958        assert_eq!(
1959            h.render_template(
1960                r#"{{datetime from_timestamp="618658211" with_month="11" to_timestamp=true}}"#,
1961                &String::new()
1962            )
1963            .expect("Render error"),
1964            comparison,
1965            "Failed to render timestamp from timestamp with month 11"
1966        );
1967
1968        let comparison = DateTime::from_timestamp(618658211, 0)
1969            .unwrap()
1970            .with_month0(0)
1971            .unwrap()
1972            .timestamp()
1973            .to_string();
1974        assert_eq!(
1975            h.render_template(
1976                r#"{{datetime from_timestamp="618658211" with_month0="0" to_timestamp=true}}"#,
1977                &String::new()
1978            )
1979            .expect("Render error"),
1980            comparison,
1981            "Failed to render timestamp from timestamp with month0 0"
1982        );
1983
1984        let comparison = DateTime::from_timestamp(618658211, 0)
1985            .unwrap()
1986            .with_day(11)
1987            .unwrap()
1988            .timestamp()
1989            .to_string();
1990        assert_eq!(
1991            h.render_template(
1992                r#"{{datetime from_timestamp="618658211" with_day="11" to_timestamp=true}}"#,
1993                &String::new()
1994            )
1995            .expect("Render error"),
1996            comparison,
1997            "Failed to render timestamp from timestamp with day 11"
1998        );
1999
2000        let comparison = DateTime::from_timestamp(618658211, 0)
2001            .unwrap()
2002            .with_day0(12)
2003            .unwrap()
2004            .timestamp()
2005            .to_string();
2006        assert_eq!(
2007            h.render_template(
2008                r#"{{datetime from_timestamp="618658211" with_day0="12" to_timestamp=true}}"#,
2009                &String::new()
2010            )
2011            .expect("Render error"),
2012            comparison,
2013            "Failed to render timestamp from timestamp with day0 11"
2014        );
2015
2016        let comparison = DateTime::from_timestamp(618658211, 0)
2017            .unwrap()
2018            .with_hour(16)
2019            .unwrap()
2020            .timestamp()
2021            .to_string();
2022        assert_eq!(
2023            h.render_template(
2024                r#"{{datetime from_timestamp="618658211" with_hour="16" to_timestamp=true}}"#,
2025                &String::new()
2026            )
2027            .expect("Render error"),
2028            comparison,
2029            "Failed to render timestamp from timestamp with hour 16"
2030        );
2031
2032        let comparison = DateTime::from_timestamp(618658211, 0)
2033            .unwrap()
2034            .with_minute(12)
2035            .unwrap()
2036            .timestamp()
2037            .to_string();
2038        assert_eq!(
2039            h.render_template(
2040                r#"{{datetime from_timestamp="618658211" with_minute="12" to_timestamp=true}}"#,
2041                &String::new()
2042            )
2043            .expect("Render error"),
2044            comparison,
2045            "Failed to render timestamp from timestamp with minute 12"
2046        );
2047
2048        let comparison = DateTime::from_timestamp(618658211, 0)
2049            .unwrap()
2050            .with_second(30)
2051            .unwrap()
2052            .timestamp()
2053            .to_string();
2054        assert_eq!(
2055            h.render_template(
2056                r#"{{datetime from_timestamp="618658211" with_second="30" to_timestamp=true}}"#,
2057                &String::new()
2058            )
2059            .expect("Render error"),
2060            comparison,
2061            "Failed to render timestamp from timestamp with second 30"
2062        );
2063
2064        let comparison = DateTime::from_timestamp(618658211, 0)
2065            .unwrap()
2066            .with_nanosecond(123456789)
2067            .unwrap()
2068            .timestamp_nanos_opt()
2069            .unwrap()
2070            .to_string();
2071        assert_eq!(
2072            h.render_template(
2073                r#"{{datetime from_timestamp="618658211" with_nanosecond="123456789" to_timestamp_nanos=true}}"#,
2074                &String::new()
2075            )
2076            .expect("Render error"),
2077            comparison,
2078            "Failed to render timestamp from timestamp with nano-second 123456789"
2079        );
2080
2081        let comparison = DateTime::from_timestamp(618658211, 0)
2082            .unwrap()
2083            .checked_add_months(Months::new(24))
2084            .unwrap()
2085            .timestamp()
2086            .to_string();
2087        assert_eq!(
2088            h.render_template(
2089                r#"{{datetime from_timestamp="618658211" add_months="24" to_timestamp=true}}"#,
2090                &String::new()
2091            )
2092            .expect("Render error"),
2093            comparison,
2094            "Failed to render timestamp from timestamp plus 24 months"
2095        );
2096
2097        let comparison = DateTime::from_timestamp(618658211, 0)
2098            .unwrap()
2099            .checked_add_signed(TimeDelta::try_weeks(4).unwrap())
2100            .unwrap()
2101            .timestamp()
2102            .to_string();
2103        assert_eq!(
2104            h.render_template(
2105                r#"{{datetime from_timestamp="618658211" add_weeks="4" to_timestamp=true}}"#,
2106                &String::new()
2107            )
2108            .expect("Render error"),
2109            comparison,
2110            "Failed to render timestamp from timestamp plus 4 weeks"
2111        );
2112
2113        let comparison = DateTime::from_timestamp(618658211, 0)
2114            .unwrap()
2115            .checked_add_signed(TimeDelta::try_days(2).unwrap())
2116            .unwrap()
2117            .timestamp()
2118            .to_string();
2119        assert_eq!(
2120            h.render_template(
2121                r#"{{datetime from_timestamp="618658211" add_days="2" to_timestamp=true}}"#,
2122                &String::new()
2123            )
2124            .expect("Render error"),
2125            comparison,
2126            "Failed to render timestamp from timestamp plus 2 days"
2127        );
2128
2129        let comparison = DateTime::from_timestamp(618658211, 0)
2130            .unwrap()
2131            .checked_add_signed(TimeDelta::try_hours(8).unwrap())
2132            .unwrap()
2133            .timestamp()
2134            .to_string();
2135        assert_eq!(
2136            h.render_template(
2137                r#"{{datetime from_timestamp="618658211" add_hours="8" to_timestamp=true}}"#,
2138                &String::new()
2139            )
2140            .expect("Render error"),
2141            comparison,
2142            "Failed to render timestamp from timestamp plus 8 hours"
2143        );
2144
2145        let comparison = DateTime::from_timestamp(618658211, 0)
2146            .unwrap()
2147            .checked_add_signed(TimeDelta::try_minutes(42).unwrap())
2148            .unwrap()
2149            .timestamp()
2150            .to_string();
2151        assert_eq!(
2152            h.render_template(
2153                r#"{{datetime from_timestamp="618658211" add_minutes="42" to_timestamp=true}}"#,
2154                &String::new()
2155            )
2156            .expect("Render error"),
2157            comparison,
2158            "Failed to render timestamp from timestamp plus 42 minutes"
2159        );
2160
2161        let comparison = DateTime::from_timestamp(618658211, 0)
2162            .unwrap()
2163            .checked_add_signed(TimeDelta::try_seconds(7).unwrap())
2164            .unwrap()
2165            .timestamp()
2166            .to_string();
2167        assert_eq!(
2168            h.render_template(
2169                r#"{{datetime from_timestamp="618658211" add_seconds="7" to_timestamp=true}}"#,
2170                &String::new()
2171            )
2172            .expect("Render error"),
2173            comparison,
2174            "Failed to render timestamp from timestamp plus 7 seconds"
2175        );
2176
2177        let comparison = DateTime::from_timestamp(618658211, 0)
2178            .unwrap()
2179            .checked_add_signed(TimeDelta::try_milliseconds(42).unwrap())
2180            .unwrap()
2181            .timestamp_millis()
2182            .to_string();
2183        assert_eq!(
2184            h.render_template(
2185                r#"{{datetime from_timestamp="618658211" add_milliseconds="42" to_timestamp_millis=true}}"#,
2186                &String::new()
2187            )
2188            .expect("Render error"),
2189            comparison,
2190            "Failed to render timestamp from timestamp plus 42 milli-seconds"
2191        );
2192
2193        let comparison = DateTime::from_timestamp(618658211, 0)
2194            .unwrap()
2195            .checked_add_signed(TimeDelta::microseconds(123))
2196            .unwrap()
2197            .timestamp_micros()
2198            .to_string();
2199        assert_eq!(
2200            h.render_template(
2201                r#"{{datetime from_timestamp="618658211" add_microseconds="123" to_timestamp_micros=true}}"#,
2202                &String::new()
2203            )
2204            .expect("Render error"),
2205            comparison,
2206            "Failed to render timestamp from timestamp plus 123 micro-seconds"
2207        );
2208
2209        let comparison = DateTime::from_timestamp(618658211, 0)
2210            .unwrap()
2211            .checked_add_signed(TimeDelta::nanoseconds(123456789))
2212            .unwrap()
2213            .timestamp_nanos_opt()
2214            .unwrap()
2215            .to_string();
2216        assert_eq!(
2217            h.render_template(
2218                r#"{{datetime from_timestamp="618658211" add_nanoseconds="123456789" to_timestamp_nanos=true}}"#,
2219                &String::new()
2220            )
2221            .expect("Render error"),
2222            comparison,
2223            "Failed to render timestamp from timestamp plus 123456789 nano-seconds"
2224        );
2225
2226        let comparison = DateTime::from_timestamp(618658211, 0)
2227            .unwrap()
2228            .checked_sub_months(Months::new(24))
2229            .unwrap()
2230            .timestamp()
2231            .to_string();
2232        assert_eq!(
2233            h.render_template(
2234                r#"{{datetime from_timestamp="618658211" sub_months="24" to_timestamp=true}}"#,
2235                &String::new()
2236            )
2237            .expect("Render error"),
2238            comparison,
2239            "Failed to render timestamp from timestamp minus 24 months"
2240        );
2241
2242        let comparison = DateTime::from_timestamp(618658211, 0)
2243            .unwrap()
2244            .checked_sub_signed(TimeDelta::try_weeks(4).unwrap())
2245            .unwrap()
2246            .timestamp()
2247            .to_string();
2248        assert_eq!(
2249            h.render_template(
2250                r#"{{datetime from_timestamp="618658211" sub_weeks="4" to_timestamp=true}}"#,
2251                &String::new()
2252            )
2253            .expect("Render error"),
2254            comparison,
2255            "Failed to render timestamp from timestamp minus 4 weeks"
2256        );
2257
2258        let comparison = DateTime::from_timestamp(618658211, 0)
2259            .unwrap()
2260            .checked_sub_signed(TimeDelta::try_days(2).unwrap())
2261            .unwrap()
2262            .timestamp()
2263            .to_string();
2264        assert_eq!(
2265            h.render_template(
2266                r#"{{datetime from_timestamp="618658211" sub_days="2" to_timestamp=true}}"#,
2267                &String::new()
2268            )
2269            .expect("Render error"),
2270            comparison,
2271            "Failed to render timestamp from timestamp minus 2 days"
2272        );
2273
2274        let comparison = DateTime::from_timestamp(618658211, 0)
2275            .unwrap()
2276            .checked_sub_signed(TimeDelta::try_hours(8).unwrap())
2277            .unwrap()
2278            .timestamp()
2279            .to_string();
2280        assert_eq!(
2281            h.render_template(
2282                r#"{{datetime from_timestamp="618658211" sub_hours="8" to_timestamp=true}}"#,
2283                &String::new()
2284            )
2285            .expect("Render error"),
2286            comparison,
2287            "Failed to render timestamp from timestamp minus 8 hours"
2288        );
2289
2290        let comparison = DateTime::from_timestamp(618658211, 0)
2291            .unwrap()
2292            .checked_sub_signed(TimeDelta::try_minutes(42).unwrap())
2293            .unwrap()
2294            .timestamp()
2295            .to_string();
2296        assert_eq!(
2297            h.render_template(
2298                r#"{{datetime from_timestamp="618658211" sub_minutes="42" to_timestamp=true}}"#,
2299                &String::new()
2300            )
2301            .expect("Render error"),
2302            comparison,
2303            "Failed to render timestamp from timestamp minus 42 minutes"
2304        );
2305
2306        let comparison = DateTime::from_timestamp(618658211, 0)
2307            .unwrap()
2308            .checked_sub_signed(TimeDelta::try_seconds(7).unwrap())
2309            .unwrap()
2310            .timestamp()
2311            .to_string();
2312        assert_eq!(
2313            h.render_template(
2314                r#"{{datetime from_timestamp="618658211" sub_seconds="7" to_timestamp=true}}"#,
2315                &String::new()
2316            )
2317            .expect("Render error"),
2318            comparison,
2319            "Failed to render timestamp from timestamp minus 7 seconds"
2320        );
2321
2322        let comparison = DateTime::from_timestamp(618658211, 0)
2323            .unwrap()
2324            .checked_sub_signed(TimeDelta::try_milliseconds(42).unwrap())
2325            .unwrap()
2326            .timestamp_millis()
2327            .to_string();
2328        assert_eq!(
2329            h.render_template(
2330                r#"{{datetime from_timestamp="618658211" sub_milliseconds="42" to_timestamp_millis=true}}"#,
2331                &String::new()
2332            )
2333            .expect("Render error"),
2334            comparison,
2335            "Failed to render timestamp from timestamp minus 42 milli-seconds"
2336        );
2337
2338        let comparison = DateTime::from_timestamp(618658211, 0)
2339            .unwrap()
2340            .checked_sub_signed(TimeDelta::microseconds(123))
2341            .unwrap()
2342            .timestamp_micros()
2343            .to_string();
2344        assert_eq!(
2345            h.render_template(
2346                r#"{{datetime from_timestamp="618658211" sub_microseconds="123" to_timestamp_micros=true}}"#,
2347                &String::new()
2348            )
2349            .expect("Render error"),
2350            comparison,
2351            "Failed to render timestamp from timestamp minus 123 micro-seconds"
2352        );
2353
2354        let comparison = DateTime::from_timestamp(618658211, 0)
2355            .unwrap()
2356            .checked_sub_signed(TimeDelta::nanoseconds(123456789))
2357            .unwrap()
2358            .timestamp_nanos_opt()
2359            .unwrap()
2360            .to_string();
2361        assert_eq!(
2362            h.render_template(
2363                r#"{{datetime from_timestamp="618658211" sub_nanoseconds="123456789" to_timestamp_nanos=true}}"#,
2364                &String::new()
2365            )
2366            .expect("Render error"),
2367            comparison,
2368            "Failed to render timestamp from timestamp minus 123456789 nano-seconds"
2369        );
2370    }
2371
2372    #[test]
2373    fn it_craps() {
2374        use handlebars::Handlebars;
2375
2376        let mut h = Handlebars::new();
2377        h.register_helper("datetime", Box::new(HandlebarsChronoDateTime));
2378
2379        assert!(
2380            matches!(
2381                h.render_template(
2382                    r#"{{datetime from_timestamp="618658ergwerg211" to_timestamp=true}}"#,
2383                    &String::new()
2384                ),
2385                Err(_e)
2386            ),
2387            "Failed to produce error with invalid timestamp"
2388        );
2389
2390        assert!(
2391            matches!(
2392                h.render_template(
2393                    r#"{{datetime from_timestamp_millis="618658ergwerg211123" to_timestamp=true}}"#,
2394                    &String::new()
2395                ),
2396                Err(_e)
2397            ),
2398            "Failed to produce error with invalid timestamp in milli-seconds"
2399        );
2400
2401        assert!(
2402            matches!(
2403                h.render_template(
2404                    r#"{{datetime from_timestamp_micros="618658ergwerg211123" to_timestamp=true}}"#,
2405                    &String::new()
2406                ),
2407                Err(_e)
2408            ),
2409            "Failed to produce error with invalid timestamp in micro-seconds"
2410        );
2411
2412        assert!(
2413            matches!(
2414                h.render_template(
2415                    r#"{{datetime from_timestamp_micros="12345678901234567890" to_timestamp=true}}"#,
2416                    &String::new()
2417                ),
2418                Err(_e)
2419            ),
2420            "Failed to produce error with invalid timestamp in micro-seconds"
2421        );
2422
2423        assert!(
2424            matches!(
2425                h.render_template(
2426                    r#"{{datetime from_timestamp_nanos="618658ergwerg211123123" to_timestamp=true}}"#,
2427                    &String::new()
2428                ),
2429                Err(_e)
2430            ),
2431            "Failed to produce error with invalid timestamp in nano-seconds"
2432        );
2433
2434        assert!(
2435            matches!(
2436                h.render_template(
2437                    r#"{{datetime from_timestamp_nanos="12345678901234567890" to_timestamp=true}}"#,
2438                    &String::new()
2439                ),
2440                Err(_e)
2441            ),
2442            "Failed to produce error with invalid timestamp in nano-seconds"
2443        );
2444
2445        assert!(
2446            matches!(
2447                h.render_template(
2448                    r#"{{datetime from_rfc2822="Wed, 09 AAA 1989 09:30:11 +0200" to_timestamp=true}}"#,
2449                    &String::new()
2450                ),
2451                Err(_e)
2452            ),
2453            "Failed to produce error with invalid RFC2822"
2454        );
2455
2456        assert!(
2457            matches!(
2458                h.render_template(
2459                    r#"{{datetime from_rfc3339="1985_06_16T12_00_00Z" to_timestamp=true}}"#,
2460                    &String::new()
2461                ),
2462                Err(_e)
2463            ),
2464            "Failed to produce error with invalid RFC3339"
2465        );
2466
2467        assert!(
2468            matches!(
2469                h.render_template(r#"{{datetime from_str="1985-06-16T12:00:00Z" to_timestamp=true}}"#, &String::new()),
2470                Err(_e)
2471            ),
2472            "Failed to produce error with invalid datetime str and format"
2473        );
2474
2475        assert!(
2476            matches!(
2477                h.render_template(
2478                    r#"{{datetime from_str="1985-06-16T12:00:00" input_format="%Y-%m-%d %H:%M:%S" to_timestamp=true}}"#,
2479                    &String::new()
2480                ),
2481                Err(_e)
2482            ),
2483            "Failed to produce error with invalid datetime str and format"
2484        );
2485
2486        //
2487
2488        #[cfg(feature = "locale")]
2489        assert!(
2490            matches!(
2491                h.render_template(r#"{{datetime output_format="%A, %B %C" locale="GAGA"}}"#, &String::new()),
2492                Err(_e)
2493            ),
2494            "Failed to produce error with invalid locale"
2495        );
2496
2497        assert!(
2498            matches!(
2499                h.render_template(
2500                    r#"{{datetime from_timestamp="618658211" years_since="1985-06-16 12:00:00"}}"#,
2501                    &String::new()
2502                ),
2503                Err(_e)
2504            ),
2505            "Failed to produce error with invalid years since base date"
2506        );
2507
2508        assert!(
2509            matches!(
2510                h.render_template(
2511                    r#"{{datetime from_rfc3339="1989-08-09T09:30:11+02:00" with_timezone="-2500"}}"#,
2512                    &String::new()
2513                ),
2514                Err(_e),
2515            ),
2516            "Failed to produce error with invalid offset"
2517        );
2518
2519        assert!(
2520            matches!(
2521                h.render_template(
2522                    r#"{{datetime from_rfc3339="1989-08-09T09:30:11+02:00" with_timezone="Plovdiv"}}"#,
2523                    &String::new()
2524                ),
2525                Err(_e),
2526            ),
2527            "Failed to produce error with invalid timezone name"
2528        );
2529
2530        assert!(
2531            matches!(
2532                h.render_template(
2533                    r#"{{datetime from_rfc3339="1989-08-09T09:30:11+02:00" with_ordinal="above9000"}}"#,
2534                    &String::new()
2535                ),
2536                Err(_e),
2537            ),
2538            "Failed to produce error with invalid ordinal"
2539        );
2540
2541        assert!(
2542            matches!(
2543                h.render_template(
2544                    r#"{{datetime from_rfc3339="1989-08-09T09:30:11+02:00" with_ordinal0="above9000"}}"#,
2545                    &String::new()
2546                ),
2547                Err(_e),
2548            ),
2549            "Failed to produce error with invalid ordinal0"
2550        );
2551
2552        assert!(
2553            matches!(
2554                h.render_template(
2555                    r#"{{datetime from_rfc3339="1989-08-09T09:30:11+02:00" with_year="above9000"}}"#,
2556                    &String::new()
2557                ),
2558                Err(_e),
2559            ),
2560            "Failed to produce error with invalid year"
2561        );
2562
2563        assert!(
2564            matches!(
2565                h.render_template(
2566                    r#"{{datetime from_rfc3339="1989-08-09T09:30:11+02:00" with_month="June"}}"#,
2567                    &String::new()
2568                ),
2569                Err(_e),
2570            ),
2571            "Failed to produce error with invalid month"
2572        );
2573
2574        assert!(
2575            matches!(
2576                h.render_template(
2577                    r#"{{datetime from_rfc3339="1989-08-09T09:30:11+02:00" with_month0="June"}}"#,
2578                    &String::new()
2579                ),
2580                Err(_e),
2581            ),
2582            "Failed to produce error with invalid month0"
2583        );
2584
2585        assert!(
2586            matches!(
2587                h.render_template(
2588                    r#"{{datetime from_rfc3339="1989-08-09T09:30:11+02:00" with_day="1st"}}"#,
2589                    &String::new()
2590                ),
2591                Err(_e),
2592            ),
2593            "Failed to produce error with invalid day"
2594        );
2595
2596        assert!(
2597            matches!(
2598                h.render_template(
2599                    r#"{{datetime from_rfc3339="1989-08-09T09:30:11+02:00" with_day0="2nd"}}"#,
2600                    &String::new()
2601                ),
2602                Err(_e),
2603            ),
2604            "Failed to produce error with invalid day0"
2605        );
2606
2607        assert!(
2608            matches!(
2609                h.render_template(
2610                    r#"{{datetime from_rfc3339="1989-08-09T09:30:11+02:00" with_hour="noon"}}"#,
2611                    &String::new()
2612                ),
2613                Err(_e),
2614            ),
2615            "Failed to produce error with invalid hour"
2616        );
2617
2618        assert!(
2619            matches!(
2620                h.render_template(
2621                    r#"{{datetime from_rfc3339="1989-08-09T09:30:11+02:00" with_minute="last"}}"#,
2622                    &String::new()
2623                ),
2624                Err(_e),
2625            ),
2626            "Failed to produce error with invalid minute"
2627        );
2628
2629        assert!(
2630            matches!(
2631                h.render_template(
2632                    r#"{{datetime from_rfc3339="1989-08-09T09:30:11+02:00" with_second="last"}}"#,
2633                    &String::new()
2634                ),
2635                Err(_e),
2636            ),
2637            "Failed to produce error with invalid second"
2638        );
2639
2640        assert!(
2641            matches!(
2642                h.render_template(
2643                    r#"{{datetime from_rfc3339="1989-08-09T09:30:11+02:00" with_nanosecond="last"}}"#,
2644                    &String::new()
2645                ),
2646                Err(_e),
2647            ),
2648            "Failed to produce error with invalid nanosecond"
2649        );
2650
2651        assert!(
2652            matches!(
2653                h.render_template(
2654                    r#"{{datetime from_rfc3339="1989-08-09T09:30:11+02:00" add_months="june"}}"#,
2655                    &String::new()
2656                ),
2657                Err(_e),
2658            ),
2659            "Failed to produce error with invalid month"
2660        );
2661
2662        assert!(
2663            matches!(
2664                h.render_template(
2665                    r#"{{datetime from_rfc3339="1989-08-09T09:30:11+02:00" add_weeks="many"}}"#,
2666                    &String::new()
2667                ),
2668                Err(_e),
2669            ),
2670            "Failed to produce error with invalid weeks"
2671        );
2672
2673        assert!(
2674            matches!(
2675                h.render_template(
2676                    r#"{{datetime from_rfc3339="1989-08-09T09:30:11+02:00" add_days="many"}}"#,
2677                    &String::new()
2678                ),
2679                Err(_e),
2680            ),
2681            "Failed to produce error with invalid days"
2682        );
2683
2684        assert!(
2685            matches!(
2686                h.render_template(
2687                    r#"{{datetime from_rfc3339="1989-08-09T09:30:11+02:00" add_hours="many"}}"#,
2688                    &String::new()
2689                ),
2690                Err(_e),
2691            ),
2692            "Failed to produce error with invalid hours"
2693        );
2694
2695        assert!(
2696            matches!(
2697                h.render_template(
2698                    r#"{{datetime from_rfc3339="1989-08-09T09:30:11+02:00" add_minutes="many"}}"#,
2699                    &String::new()
2700                ),
2701                Err(_e),
2702            ),
2703            "Failed to produce error with invalid minutes"
2704        );
2705
2706        assert!(
2707            matches!(
2708                h.render_template(
2709                    r#"{{datetime from_rfc3339="1989-08-09T09:30:11+02:00" add_seconds="seven"}}"#,
2710                    &String::new()
2711                ),
2712                Err(_e),
2713            ),
2714            "Failed to produce error with invalid seconds"
2715        );
2716
2717        assert!(
2718            matches!(
2719                h.render_template(
2720                    r#"{{datetime from_rfc3339="1989-08-09T09:30:11+02:00" add_milliseconds="a little"}}"#,
2721                    &String::new()
2722                ),
2723                Err(_e),
2724            ),
2725            "Failed to produce error with invalid milliseconds"
2726        );
2727
2728        assert!(
2729            matches!(
2730                h.render_template(
2731                    r#"{{datetime from_rfc3339="1989-08-09T09:30:11+02:00" add_microseconds="some micros"}}"#,
2732                    &String::new()
2733                ),
2734                Err(_e),
2735            ),
2736            "Failed to produce error with invalid microseconds"
2737        );
2738
2739        assert!(
2740            matches!(
2741                h.render_template(
2742                    r#"{{datetime from_rfc3339="1989-08-09T09:30:11+02:00" add_nanoseconds="some nanos"}}"#,
2743                    &String::new()
2744                ),
2745                Err(_e),
2746            ),
2747            "Failed to produce error with invalid nanoseconds"
2748        );
2749
2750        assert!(
2751            matches!(
2752                h.render_template(
2753                    r#"{{datetime from_rfc3339="1989-08-09T09:30:11+02:00" sub_months="june"}}"#,
2754                    &String::new()
2755                ),
2756                Err(_e),
2757            ),
2758            "Failed to produce error with invalid month"
2759        );
2760
2761        assert!(
2762            matches!(
2763                h.render_template(
2764                    r#"{{datetime from_rfc3339="1989-08-09T09:30:11+02:00" sub_weeks="many"}}"#,
2765                    &String::new()
2766                ),
2767                Err(_e),
2768            ),
2769            "Failed to produce error with invalid weeks"
2770        );
2771
2772        assert!(
2773            matches!(
2774                h.render_template(
2775                    r#"{{datetime from_rfc3339="1989-08-09T09:30:11+02:00" sub_days="many"}}"#,
2776                    &String::new()
2777                ),
2778                Err(_e),
2779            ),
2780            "Failed to produce error with invalid days"
2781        );
2782
2783        assert!(
2784            matches!(
2785                h.render_template(
2786                    r#"{{datetime from_rfc3339="1989-08-09T09:30:11+02:00" sub_hours="many"}}"#,
2787                    &String::new()
2788                ),
2789                Err(_e),
2790            ),
2791            "Failed to produce error with invalid hours"
2792        );
2793
2794        assert!(
2795            matches!(
2796                h.render_template(
2797                    r#"{{datetime from_rfc3339="1989-08-09T09:30:11+02:00" sub_minutes="many"}}"#,
2798                    &String::new()
2799                ),
2800                Err(_e),
2801            ),
2802            "Failed to produce error with invalid minutes"
2803        );
2804
2805        assert!(
2806            matches!(
2807                h.render_template(
2808                    r#"{{datetime from_rfc3339="1989-08-09T09:30:11+02:00" sub_seconds="seven"}}"#,
2809                    &String::new()
2810                ),
2811                Err(_e),
2812            ),
2813            "Failed to produce error with invalid seconds"
2814        );
2815
2816        assert!(
2817            matches!(
2818                h.render_template(
2819                    r#"{{datetime from_rfc3339="1989-08-09T09:30:11+02:00" sub_milliseconds="a little"}}"#,
2820                    &String::new()
2821                ),
2822                Err(_e),
2823            ),
2824            "Failed to produce error with invalid milliseconds"
2825        );
2826
2827        assert!(
2828            matches!(
2829                h.render_template(
2830                    r#"{{datetime from_rfc3339="1989-08-09T09:30:11+02:00" sub_microseconds="some micros"}}"#,
2831                    &String::new()
2832                ),
2833                Err(_e),
2834            ),
2835            "Failed to produce error with invalid microseconds"
2836        );
2837
2838        assert!(
2839            matches!(
2840                h.render_template(
2841                    r#"{{datetime from_rfc3339="1989-08-09T09:30:11+02:00" sub_nanoseconds="some nanos"}}"#,
2842                    &String::new()
2843                ),
2844                Err(_e),
2845            ),
2846            "Failed to produce error with invalid nanoseconds"
2847        );
2848    }
2849}