adb pull can mess up file timestamps when exporting photos from Android if you forget -a.
adb pull -a /storage/emulated/0/DCIM ./DCIM
That flag is easy to miss, but it saves a lot of cleanup later.
Some of my files were already messed up, so I used a small Python script afterward to repair the timestamps. It was vibe-coded rather than properly written, but it worked well enough for this one-off job. The script checks the filename first, then falls back to EXIF metadata via exiftool.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import re
import time
import subprocess
from datetime import datetime
def run_exiftool(file_path):
try:
cmd = ['exiftool', '-DateTimeOriginal', '-s3', file_path]
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode == 0 and result.stdout.strip():
date_str = result.stdout.strip()
try:
return datetime.strptime(date_str, "%Y:%m:%d %H:%M:%S").timestamp()
except ValueError:
print(f"Could not parse DateTimeOriginal: {date_str}")
cmd = ['exiftool', '-CreateDate', '-s3', file_path]
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode == 0 and result.stdout.strip():
date_str = result.stdout.strip()
try:
return datetime.strptime(date_str, "%Y:%m:%d %H:%M:%S").timestamp()
except ValueError:
print(f"Could not parse CreateDate: {date_str}")
cmd = ['exiftool', '-FileModifyDate', '-s3', file_path]
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode == 0 and result.stdout.strip():
date_str = result.stdout.strip()
try:
if "+" in date_str:
date_str = date_str.split("+").strip()
return datetime.strptime(date_str, "%Y:%m:%d %H:%M:%S").timestamp()
except ValueError:
print(f"Could not parse FileModifyDate: {date_str}")
except Exception as e:
print(f"Failed to read EXIF data: {file_path} - {str(e)}")
return None
def extract_timestamp_from_filename(filename):
match = re.search(r'IMG_(\d{8})_(\d{6})', filename)
if match:
date_str, time_str = match.groups()
dt_str = f"{date_str[:4]}-{date_str[4:6]}-{date_str[6:]} {time_str[:2]}:{time_str[2:4]}:{time_str[4:]}"
return datetime.strptime(dt_str, "%Y-%m-%d %H:%M:%S").timestamp()
match = re.search(r'Screenshot_(\d{4}-\d{2}-\d{2})-(\d{2})-(\d{2})-(\d{2})', filename)
if match:
year_month_day, hour, minute, second = match.groups()
dt_str = f"{year_month_day} {hour}:{minute}:{second}"
return datetime.strptime(dt_str, "%Y-%m-%d %H:%M:%S").timestamp()
match = re.search(r'VID_(\d{8})_(\d{6})', filename)
if match:
date_str, time_str = match.groups()
dt_str = f"{date_str[:4]}-{date_str[4:6]}-{date_str[6:]} {time_str[:2]}:{time_str[2:4]}:{time_str[4:]}"
return datetime.strptime(dt_str, "%Y-%m-%d %H:%M:%S").timestamp()
match = re.search(r'Screenrecorder-(\d{4}-\d{2}-\d{2}-\d{2}-\d{2}-\d{2})', filename)
if match:
datetime_str = match.group(1)
formatted_str = f"{datetime_str[:10]} {datetime_str[11:13]}:{datetime_str[14:16]}:{datetime_str[17:19]}"
return datetime.strptime(formatted_str, "%Y-%m-%d %H:%M:%S").timestamp()
match = re.search(r'Camera_XHS_(\d{13})', filename)
if match:
return int(match.group(1)) / 1000
match = re.search(r'^(\d{13})', filename)
if match:
return int(match.group(1)) / 1000
return None
def check_exiftool_installed():
try:
subprocess.run(['exiftool', '-ver'], capture_output=True, text=True)
return True
except FileNotFoundError:
return False
def fix_timestamps(directory):
count = 0
failed = 0
if not check_exiftool_installed():
print("Error: this script requires exiftool.")
print("macOS: brew install exiftool")
print("Linux: sudo apt-get install libimage-exiftool-perl")
print("Windows: https://exiftool.org/")
return
for root, dirs, files in os.walk(directory):
for filename in files:
file_path = os.path.join(root, filename)
if filename.startswith('.'):
continue
try:
timestamp = extract_timestamp_from_filename(filename)
if timestamp is None:
timestamp = run_exiftool(file_path)
if timestamp:
current_time = time.time()
if timestamp > current_time:
timestamp = current_time
os.utime(file_path, (timestamp, timestamp))
print(f"Fixed: {file_path}")
count += 1
else:
print(f"Could not extract a timestamp: {file_path}")
failed += 1
except Exception as e:
print(f"Failed to process: {file_path} - {str(e)}")
failed += 1
print(f"\nDone. Fixed: {count} files, failed: {failed} files")
if __name__ == "__main__":
import sys
if len(sys.argv) != 2:
print(f"Usage: python {sys.argv} <path_to_DCIM>")
sys.exit(1)
dcim_path = sys.argv
if not os.path.isdir(dcim_path):
print(f"Error: '{dcim_path}' is not a valid directory")
sys.exit(1)
fix_timestamps(dcim_path)