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)]
11pub 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 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 return Err(RenderErrorReason::Other("Missing `input_format` hash parameter".to_string()).into());
134 }
135 } else {
136 Utc::now()
137 };
138
139 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 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 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 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 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 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 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 #[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 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 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 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 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 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 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 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 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 #[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 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 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 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 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 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 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 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 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 #[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 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 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 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 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 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 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 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 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 #[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 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 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 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 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 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 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 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 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 #[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 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 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 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 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 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 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 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 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 #[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 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 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 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 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 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 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 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 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 #[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 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 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 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 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 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 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 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 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 #[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 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 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 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 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 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 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 #[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 #[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}