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