1use crate::{
2 ClassifiedDate, DateClassification, Dt, DtErr, DtErrKind, MAX_DATE_STRING_LEN, Mode, Order,
3 OrderFirst, ParseCfg, Scale, an_err, classify_date, default_date_parse_options,
4 generate_ambiguous_day_first_candidates, generate_ambiguous_month_first_candidates,
5 generate_ambiguous_year_first_candidates, generate_unambiguous_candidates,
6 is_week_date_missing_weekday, parse_pure_numeric_unix_timestamp, parse_syslog_no_year,
7 parse_week_date_no_weekday, parse_yyyy_mm, smart_detect_date_order, try_pure_numeric,
8};
9use alloc::borrow::Cow;
10use alloc::string::String;
11
12impl Dt {
13 pub fn from_str_parse(s: &str, opts: &Option<ParseCfg>) -> Result<Dt, DtErr> {
14 let opts: &ParseCfg = opts
15 .as_ref()
16 .unwrap_or_else(|| default_date_parse_options());
17
18 if s.is_empty() {
19 return Err(an_err!(DtErrKind::Incomplete, "empty"));
20 } else if s.len() > MAX_DATE_STRING_LEN {
21 return Err(an_err!(DtErrKind::InvalidInput, "too long: {}", s));
22 }
23
24 let lang = opts.lang;
25 let ref_time = &opts.ref_time;
26
27 let lowered: Cow<str> = if opts.to_lower {
28 Cow::Owned(s.to_lowercase())
29 } else {
30 Cow::Borrowed(s)
31 };
32
33 let classification = match classify_date(&lowered, lang, ref_time) {
34 Ok(ClassifiedDate::Parsed(time_point)) => return Ok(time_point),
35 Ok(ClassifiedDate::Cls(c)) => c,
36 Err(e) => {
37 return Err(an_err!(
39 DtErrKind::InvalidInput,
40 "{}",
41 s => e
42 ));
43 }
44 };
45 let normalized = &classification.date;
52
53 let (mode, date_order) = if let Some(formats) = &opts.parse {
54 if !formats.is_empty() {
55 for fmt in formats {
56 if let Ok(value) = Self::from_str(normalized, fmt, true, true, false) {
57 return Ok(value);
58 }
59 }
60 if opts.mode == Mode::Explicit {
62 return Err(an_err!(DtErrKind::InvalidInput, "{}", s));
63 }
64 }
65 (opts.mode, opts.order)
66 } else {
67 (opts.mode, opts.order)
68 };
69
70 if classification.is_pure_numeric {
75 match mode {
76 Mode::UnixTimestamp => {
77 if let Some(dt) = parse_pure_numeric_unix_timestamp(
78 normalized,
79 classification.num_non_decimal_digits as usize,
80 ) {
81 return Ok(dt);
82 }
83 }
84 _ => {
85 if let Some(dt) = try_pure_numeric(
86 normalized,
87 classification.num_digits,
88 classification.num_non_decimal_digits,
89 classification.is_decimal,
90 mode,
91 ) {
92 return Ok(dt);
94 }
95 }
96 }
97 }
98 if !classification.has_year
99 && let Some(dt) = parse_syslog_no_year(normalized, lang, ref_time)
100 {
101 return Ok(dt);
102 }
103
104 if is_week_date_missing_weekday(&classification) {
105 if let Some(dt) = parse_week_date_no_weekday(&classification.date, lang, ref_time) {
107 return Ok(dt);
108 }
109 }
110 if let Some(dt) = try_unambiguous(normalized, &classification) {
111 return Ok(dt);
112 }
113 if let Some(dt) = match date_order {
115 Order::Smart => {
116 let order = smart_detect_date_order(normalized, &classification);
117 let mut result: Option<Dt>;
118
119 match order {
120 OrderFirst::Day => {
121 result = try_compatible_formats(
122 normalized,
123 generate_ambiguous_day_first_candidates(&classification),
124 );
125 if result.is_none() {
128 result = try_compatible_formats(
129 normalized,
130 generate_ambiguous_month_first_candidates(&classification),
131 );
132 }
134
135 if result.is_none() {
136 result = try_compatible_formats(
137 normalized,
138 generate_ambiguous_year_first_candidates(&classification),
139 );
140 }
142 }
143 OrderFirst::Month => {
144 result = try_compatible_formats(
145 normalized,
146 generate_ambiguous_month_first_candidates(&classification),
147 );
148 if result.is_none() {
151 result = try_compatible_formats(
152 normalized,
153 generate_ambiguous_day_first_candidates(&classification),
154 );
155 }
157
158 if result.is_none() {
159 result = try_compatible_formats(
160 normalized,
161 generate_ambiguous_year_first_candidates(&classification),
162 );
163 }
165 }
166 OrderFirst::Year => {
167 result = try_compatible_formats(
168 normalized,
169 generate_ambiguous_year_first_candidates(&classification),
170 );
171 if result.is_none() {
174 result = try_compatible_formats(
175 normalized,
176 generate_ambiguous_day_first_candidates(&classification),
177 );
178 }
180
181 if result.is_none() {
182 result = try_compatible_formats(
183 normalized,
184 generate_ambiguous_month_first_candidates(&classification),
185 );
186 }
188 }
189 }
190
191 result
192 }
193 Order::Year => try_compatible_formats(
194 normalized,
195 generate_ambiguous_year_first_candidates(&classification),
196 ),
197 Order::Day => try_compatible_formats(
198 normalized,
199 generate_ambiguous_day_first_candidates(&classification),
200 ),
201 Order::Month => try_compatible_formats(
202 normalized,
203 generate_ambiguous_month_first_candidates(&classification),
204 ),
205 } {
206 return Ok(dt);
207 }
208 if classification.is_pure_numeric
210 && mode != Mode::UnixTimestamp
211 && let Some(dt) = parse_pure_numeric_unix_timestamp(
212 normalized,
213 classification.num_non_decimal_digits as usize,
214 )
215 {
216 return Ok(dt);
217 }
218 Err(an_err!(DtErrKind::InvalidInput, "{}", s))
219 }
220
221 #[inline]
227 pub fn str_to_attos(s: &str, opts: &Option<ParseCfg>) -> Option<i128> {
228 Dt::from_str_parse(s, opts).ok().map(|tp| tp.to_attos())
229 }
230
231 #[inline]
237 pub fn str_to_ms(s: &str, opts: &Option<ParseCfg>) -> Option<i128> {
238 Dt::from_str_parse(s, opts).ok().map(|tp| tp.to_ms())
239 }
240
241 #[inline]
247 pub fn str_to_unix_ms(s: &str, opts: &Option<ParseCfg>) -> Option<i128> {
248 Dt::from_str_parse(s, opts).ok().map(|tp| {
249 tp.to_scale_and_then_diff(Scale::UTC, Dt::UNIX_EPOCH)
250 .to_ms()
251 })
252 }
253}
254
255#[inline]
260pub(crate) fn try_compatible_formats<I>(s: &str, formats: I) -> Option<Dt>
261where
262 I: IntoIterator<Item = String>,
263{
264 formats
265 .into_iter()
266 .find_map(|fmt| Dt::from_str(s, &fmt, true, true, false).ok())
267}
268
269#[inline]
270pub(crate) fn try_unambiguous(s: &str, classification: &DateClassification) -> Option<Dt> {
271 if matches!(classification.bytes_len, 6..=8)
272 && let Some(dt) = parse_yyyy_mm(s.as_bytes())
273 {
274 return Some(dt);
275 }
276 try_compatible_formats(s, generate_unambiguous_candidates(classification))
277}