1111def get_image_timestamp_from_filename (img_path , raise_error = False ) -> datetime .datetime | None :
1212 """
1313 Parse the date and time a photo was taken from its filename.
14+ All times are assumed to be in the local timezone. Timezone information is ignored.
15+ The maxium precision is seconds (milliseconds are ignored).
1416
15- The timestamp must be in the format `YYYYMMDDHHMMSS` but can be
16- preceded or followed by other characters (e.g. `84-20220916202959-snapshot.jpg`).
17+ Supports various formats with flexible delimiters. Supports text prefixes and suffixes.
18+ - Consecutive digits: YYYYMMDDHHMMSS
19+ - Date and time as separate groups: YYYYMMDD and HHMMSS with any delimiter between
20+ - Delimited formats within date or time groups
1721
1822 >>> out_fmt = "%Y-%m-%d %H:%M:%S"
1923 >>> # Aarhus date format
@@ -35,19 +39,34 @@ def get_image_timestamp_from_filename(img_path, raise_error=False) -> datetime.d
3539 """
3640 name = pathlib .Path (img_path ).stem
3741 date = None
42+ strptime_format = "%Y%m%d%H%M%S"
43+
44+ # Put more specific/longer patterns first if overlap is possible.
45+ # These could be combined into one pattern, but it would be less readable.
46+ consecutive_pattern = r"\d{14}" # YYYYMMDDHHMMSS
47+ two_groups_pattern = r"\d{8}[^\d]+\d{6}" # YYYYMMDD*HHMMSS
48+ # Allow single non-digit delimiters within components, and one or more between DD and HH
49+ delimited_pattern = r"\d{4}[^\d]\d{2}[^\d]\d{2}[^\d]+\d{2}[^\d]\d{2}[^\d]\d{2}" # YYYY*MM*DD*+HH*MM*SS
50+
51+ # Combine patterns with OR '|' but keep them in their own groups
52+ pattern = re .compile (f"({ consecutive_pattern } )|({ two_groups_pattern } )|({ delimited_pattern } )" )
53+
54+ match = pattern .search (name )
55+ if match :
56+ # Get the full string matched by any of the patterns
57+ matched_string = match .group (0 )
58+ # Remove all non-digit characters to create YYYYMMDDHHMMSS
59+ consecutive_date_string = re .sub (r"[^\d]" , "" , matched_string )
3860
39- # Extract date from a filename using regex in the format %Y%m%d%H%M%S
40- matches = re .search (r"(\d{14})" , name )
41- if matches :
4261 try :
43- date = datetime .datetime .strptime (matches . group (), "%Y%m%d%H%M%S" )
62+ date = datetime .datetime .strptime (consecutive_date_string , strptime_format )
4463 except ValueError :
4564 pass
4665
4766 if not date :
4867 try :
4968 date = dateutil .parser .parse (name , fuzzy = False ) # Fuzzy will interpret "DSC_1974" as 1974-01-01
50- except dateutil .parser .ParserError :
69+ except ( dateutil .parser .ParserError , ValueError , OverflowError ) :
5170 pass
5271
5372 if not date and raise_error :
0 commit comments