1use chrono::NaiveDate;
5use std::path::Path;
6
7#[cfg(test)]
8mod tests;
9
10fn fti_digits(s: &str) -> bool {
11 s.len() >= 2 && s.chars().take(2).all(|i| i.is_ascii_digit())
12}
13
14pub fn parse(par: &str, fin: &str) -> Option<NaiveDate> {
16 if !fti_digits(fin) || par.len() < 4 {
17 return None;
18 }
19
20 let y = par.parse::<i32>().ok()?;
21 let m = fin[..2].parse::<u32>().unwrap();
22
23 let mut dayinf = &fin[2..];
25 if dayinf.starts_with(|i| matches!(i, '-' | '_')) {
26 dayinf = &dayinf[1..];
27 }
28 if !fti_digits(dayinf) {
29 return None;
30 }
31 let d = dayinf[..2].parse::<u32>().unwrap();
32
33 chrono::NaiveDate::from_ymd_opt(y, m, d)
34}
35
36pub fn parse_from_path(x: &Path) -> Option<NaiveDate> {
39 let mut fin = x;
40 let mut fins = fin.file_name()?.to_str()?;
41
42 if !fti_digits(fins) {
44 fin = x.parent()?;
45 fins = fin.file_name()?.to_str()?;
46 if !fti_digits(fins) {
47 return None;
48 }
49 }
50
51 let par = fin.parent()?.file_name()?.to_str()?;
52 parse(par, fins)
53}
54
55#[cfg(feature = "camino")]
59pub fn parse_from_utf8path(x: &camino::Utf8Path) -> Option<NaiveDate> {
60 let mut fin = x;
61 let mut fins = fin.file_name()?;
62
63 if !fti_digits(fins) {
65 fin = x.parent()?;
66 fins = fin.file_name()?;
67 if !fti_digits(fins) {
68 return None;
69 }
70 }
71
72 let par = fin.parent()?.file_name()?;
73 parse(par, fins)
74}
75
76pub fn fmt(date: &NaiveDate, daysep: Option<char>) -> String {
77 let tmp;
78 date.format(if let Some(daysep) = daysep {
79 assert!(matches!(daysep, '-' | '_'));
80 tmp = format!("%Y/%m{}%d", daysep);
81 &tmp
82 } else {
83 "%Y/%m%d"
84 })
85 .to_string()
86}