1use chrono::prelude::{DateTime, Utc};
2
3mod chrono_english {
4 pub mod errors;
5 pub mod lib;
6 pub mod parser;
7 pub mod types;
8}
9
10use chrono_english::lib::{parse_date_string, DateError, Dialect};
11
12const DIALECT: Dialect = Dialect::Us;
13
14fn append_min_if_only_hour(input: &str) -> String {
16 if input.len() >= 3 {
17 let last_three = &input[input.len() - 3..];
18
19 if last_three.starts_with('+')
21 && last_three.chars().nth(1).map(|ch| ch.is_ascii_digit()) == Some(true)
22 && last_three.chars().nth(2).map(|ch| ch.is_ascii_digit()) == Some(true)
23 {
24 return format!("{input}:00");
25 }
26 }
27
28 if input.len() >= 2 {
29 let last_two_char = &input[input.len() - 2..];
30
31 if last_two_char.starts_with('+') {
32 let last_char_opt = last_two_char.chars().nth(1);
33 if let Some(last_char) = last_char_opt {
34 if last_char.is_ascii_digit() {
35 let without_last_two = &input[..input.len() - 2];
36 return format!("{without_last_two}+0{last_char}:00");
37 }
38 }
39 }
40 }
41
42 input.to_string()
43}
44
45pub fn parse_date_args(
47 args: &[String],
48 now: DateTime<Utc>,
49) -> Result<DateTime<Utc>, DateError> {
50 let args_combined = append_min_if_only_hour(
52 {
53 let combined = args.join(" ");
54 if let Some(stripped) = combined.strip_prefix("in a ") {
55 format!("1 {stripped}")
56 }
57 else if let Some(stripped) = combined.strip_prefix("in an ") {
58 format!("1 {stripped}")
59 }
60 else if let Some(stripped) = combined.strip_prefix("in ") {
61 stripped.to_string()
62 }
63 else if let Some(stripped) = combined.strip_prefix("at ") {
64 stripped.to_string()
65 }
66 else {
67 combined
68 }
69 }
70 .trim(),
71 );
72
73 if args_combined.chars().all(|c| c.is_ascii_digit()) {
75 if let Ok(timestamp) = args_combined.parse::<i64>() {
76 if args_combined.len() == 13 && timestamp / 1000 >= 1_000_000_000 {
81 let seconds = timestamp / 1000;
82 let nanoseconds = (timestamp % 1000) * 1_000_000;
83 if let Some(datetime) =
84 DateTime::from_timestamp(seconds, nanoseconds as u32)
85 {
86 return Ok(datetime);
87 }
88 }
89
90 if let Some(datetime) = DateTime::from_timestamp(timestamp, 0) {
92 return Ok(datetime);
93 }
94 }
95 }
96
97 DateTime::parse_from_rfc2822(&args_combined)
98 .or_else(|_| DateTime::parse_from_rfc3339(&args_combined))
99 .map(|datetime| datetime.with_timezone(&Utc))
100 .or_else(|_| parse_date_string(&args_combined, now, DIALECT))
101}
102
103pub fn to_iso(date: DateTime<Utc>) -> String {
104 date.to_rfc3339().replace("+00:00", "Z")
105}
106
107pub fn parse_print(now: DateTime<Utc>, s: &str) -> String {
108 to_iso(parse_date_string(s, now, DIALECT).unwrap())
109}