1use super::parse::{match_multi_number, MONTH_NAMES, WEEKDAY_NAMES};
4use super::tm::{get_time_sec, match_string, parse_timestamp_prefix};
5use libc::{time_t, tm};
6use std::mem::MaybeUninit;
7
8fn update_tm(tm: &mut tm, now: &tm, sec: i64) -> time_t {
9 if tm.tm_mday < 0 {
10 tm.tm_mday = now.tm_mday;
11 }
12 if tm.tm_mon < 0 {
13 tm.tm_mon = now.tm_mon;
14 }
15 if tm.tm_year < 0 {
16 tm.tm_year = now.tm_year;
17 if tm.tm_mon > now.tm_mon {
18 tm.tm_year -= 1;
19 }
20 }
21 unsafe {
22 let t = libc::mktime(tm);
23 if t == -1 {
24 return -1;
25 }
26 let n = t - sec;
27 let mut out = MaybeUninit::<tm>::uninit();
28 let p = libc::localtime_r(&n, out.as_mut_ptr());
29 if p.is_null() {
30 return -1;
31 }
32 *tm = *p;
33 n
34 }
35}
36
37fn pending_number(tm: &mut tm, num: &mut i32) {
38 let number = *num;
39 if number != 0 {
40 *num = 0;
41 if tm.tm_mday < 0 && number < 32 {
42 tm.tm_mday = number;
43 } else if tm.tm_mon < 0 && number < 13 {
44 tm.tm_mon = number - 1;
45 } else if tm.tm_year < 0 {
46 if (1969..2100).contains(&number) {
47 tm.tm_year = number - 1900;
48 } else if (69..100).contains(&number) {
49 tm.tm_year = number;
50 } else if number < 38 {
51 tm.tm_year = number + 100;
52 }
53 }
54 }
55}
56
57fn date_now(tm: &mut tm, now: &tm, num: &mut i32) {
58 *num = 0;
59 let _ = update_tm(tm, now, 0);
60}
61
62fn date_yesterday(tm: &mut tm, now: &tm, num: &mut i32) {
63 *num = 0;
64 let _ = update_tm(tm, now, 24 * 60 * 60);
65}
66
67fn date_time(tm: &mut tm, now: &tm, hour: i32) {
68 if tm.tm_hour < hour {
69 let _ = update_tm(tm, now, 24 * 60 * 60);
70 }
71 tm.tm_hour = hour;
72 tm.tm_min = 0;
73 tm.tm_sec = 0;
74}
75
76fn date_midnight(tm: &mut tm, now: &tm, num: &mut i32) {
77 pending_number(tm, num);
78 date_time(tm, now, 0);
79}
80
81fn date_noon(tm: &mut tm, now: &tm, num: &mut i32) {
82 pending_number(tm, num);
83 date_time(tm, now, 12);
84}
85
86fn date_tea(tm: &mut tm, now: &tm, num: &mut i32) {
87 pending_number(tm, num);
88 date_time(tm, now, 17);
89}
90
91fn date_pm(tm: &mut tm, _now: &tm, num: &mut i32) {
92 let n = *num;
93 *num = 0;
94 let mut hour = tm.tm_hour;
95 if n != 0 {
96 hour = n;
97 tm.tm_min = 0;
98 tm.tm_sec = 0;
99 }
100 tm.tm_hour = (hour % 12) + 12;
101}
102
103fn date_am(tm: &mut tm, _now: &tm, num: &mut i32) {
104 let n = *num;
105 *num = 0;
106 let mut hour = tm.tm_hour;
107 if n != 0 {
108 hour = n;
109 tm.tm_min = 0;
110 tm.tm_sec = 0;
111 }
112 tm.tm_hour = hour % 12;
113}
114
115fn date_never(tm: &mut tm, _now: &tm, num: &mut i32) {
116 let n: time_t = 0;
117 unsafe {
118 let mut out = MaybeUninit::<tm>::uninit();
119 let p = libc::localtime_r(&n, out.as_mut_ptr());
120 if !p.is_null() {
121 *tm = *p;
122 }
123 }
124 *num = 0;
125}
126
127const NUMBER_NAME: [&str; 11] = [
128 "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten",
129];
130
131const TYPELEN: [(&str, i64); 5] = [
132 ("seconds", 1),
133 ("minutes", 60),
134 ("hours", 60 * 60),
135 ("days", 24 * 60 * 60),
136 ("weeks", 7 * 24 * 60 * 60),
137];
138
139fn alpha_word_len(date: &[u8]) -> usize {
140 let mut i = 0usize;
141 while i < date.len() && date[i].is_ascii_alphabetic() {
142 i += 1;
143 }
144 i
145}
146
147fn approxidate_alpha(
148 date: &[u8],
149 tm: &mut tm,
150 now: &tm,
151 num: &mut i32,
152 touched: &mut i32,
153) -> usize {
154 let end = alpha_word_len(date);
155
156 for (i, name) in MONTH_NAMES.iter().enumerate() {
157 let m = match_string(date, name);
158 if m >= 3 {
159 tm.tm_mon = i as i32;
160 *touched = 1;
161 return end;
162 }
163 }
164
165 let specials: [(&str, fn(&mut tm, &tm, &mut i32)); 8] = [
166 ("yesterday", date_yesterday),
167 ("noon", date_noon),
168 ("midnight", date_midnight),
169 ("tea", date_tea),
170 ("PM", date_pm),
171 ("AM", date_am),
172 ("never", date_never),
173 ("now", date_now),
174 ];
175 for (name, f) in specials {
176 if match_string(date, name) == name.len() {
177 f(tm, now, num);
178 *touched = 1;
179 return end;
180 }
181 }
182
183 if *num == 0 {
184 for i in 1..11 {
185 let len = NUMBER_NAME[i].len();
186 if match_string(date, NUMBER_NAME[i]) == len {
187 *num = i as i32;
188 *touched = 1;
189 return end;
190 }
191 }
192 if match_string(date, "last") == 4 {
193 *num = 1;
194 *touched = 1;
195 }
196 return end;
197 }
198
199 for (typ, len_secs) in TYPELEN {
200 let tlen = typ.len();
201 if match_string(date, typ) >= tlen.saturating_sub(1) {
202 let _ = update_tm(tm, now, len_secs * (*num as i64));
203 *num = 0;
204 *touched = 1;
205 return end;
206 }
207 }
208
209 for (i, wname) in WEEKDAY_NAMES.iter().enumerate() {
210 let m = match_string(date, wname);
211 if m >= 3 {
212 let mut n = *num - 1;
213 *num = 0;
214 let mut diff = tm.tm_wday - (i as i32);
215 if diff <= 0 {
216 n += 1;
217 }
218 diff += 7 * n;
219 let _ = update_tm(tm, now, (diff as i64) * 24 * 60 * 60);
220 *touched = 1;
221 return end;
222 }
223 }
224
225 if match_string(date, "months") >= 5 {
226 let _ = update_tm(tm, now, 0);
227 let mut n = tm.tm_mon - *num;
228 *num = 0;
229 while n < 0 {
230 n += 12;
231 tm.tm_year -= 1;
232 }
233 tm.tm_mon = n;
234 *touched = 1;
235 return end;
236 }
237
238 if match_string(date, "years") >= 4 {
239 let _ = update_tm(tm, now, 0);
240 tm.tm_year -= *num;
241 *num = 0;
242 *touched = 1;
243 return end;
244 }
245
246 end
247}
248
249fn approxidate_digit(date: &[u8], tm: &mut tm, num: &mut i32, now_sec: i64) -> usize {
250 let (number, n) = parse_timestamp_prefix(date);
251 if n == 0 {
252 return 0;
253 }
254 let end = n;
255
256 if let Some(&sep) = date.get(end) {
257 if matches!(sep, b':' | b'.' | b'/' | b'-')
258 && date.get(end + 1).is_some_and(|b| b.is_ascii_digit())
259 {
260 let m = match_multi_number(number, date, end, tm, now_sec);
261 if m != 0 {
262 return m;
263 }
264 }
265 }
266
267 if date[0] != b'0' || end <= 2 {
268 *num = number as i32;
269 }
270 end
271}
272
273pub fn approxidate_careful(date: &str, error_ret: Option<&mut i32>) -> u64 {
275 let mut dummy = 0;
276 let er: &mut i32 = match error_ret {
277 Some(p) => p,
278 None => &mut dummy,
279 };
280 if let Ok((ts, _)) = super::parse::parse_date_basic(date) {
281 *er = 0;
282 return ts;
283 }
284 let tv_sec = get_time_sec();
285 approxidate_str(date, tv_sec, er)
286}
287
288fn approxidate_str(date: &str, time_sec: i64, error_ret: &mut i32) -> u64 {
289 let mut tm_buf = MaybeUninit::<tm>::uninit();
290 let mut now_buf = MaybeUninit::<tm>::uninit();
291 unsafe {
292 let tt = time_sec as time_t;
293 let p = libc::localtime_r(&tt, tm_buf.as_mut_ptr());
294 if p.is_null() {
295 *error_ret = 1;
296 return 0;
297 }
298 let p2 = libc::localtime_r(&tt, now_buf.as_mut_ptr());
299 if p2.is_null() {
300 *error_ret = 1;
301 return 0;
302 }
303 let mut tm = *tm_buf.as_ptr();
304 let now = *now_buf.as_ptr();
305 tm.tm_year = -1;
306 tm.tm_mon = -1;
307 tm.tm_mday = -1;
308
309 let mut number = 0i32;
310 let mut touched = 0i32;
311 let bytes = date.as_bytes();
312 let mut i = 0usize;
313 while i < bytes.len() {
314 let c = bytes[i];
315 if c == 0 || c == b'\n' {
316 break;
317 }
318 if c.is_ascii_digit() {
319 pending_number(&mut tm, &mut number);
320 let adv = approxidate_digit(&bytes[i..], &mut tm, &mut number, time_sec);
321 if adv == 0 {
322 break;
323 }
324 i += adv;
325 touched = 1;
326 continue;
327 }
328 if c.is_ascii_alphabetic() {
329 let adv = approxidate_alpha(&bytes[i..], &mut tm, &now, &mut number, &mut touched);
330 i += adv;
331 continue;
332 }
333 i += 1;
334 }
335 pending_number(&mut tm, &mut number);
336 if touched == 0 {
337 *error_ret = 1;
338 }
339 let n = update_tm(&mut tm, &now, 0);
340 if n < 0 {
341 *error_ret = 1;
342 return 0;
343 }
344 n as u64
345 }
346}